Пример #1
0
class ShapesModel(Model):
    def __init__(self, N, width=20, height=10):
        self.running = True
        self.N = N    # num of agents
        self.headings = ((1, 0), (0, 1), (-1, 0), (0, -1))  # tuples are fast
        self.grid = SingleGrid(width, height, torus=False)
        self.schedule = RandomActivation(self)
        self.make_walker_agents()

    def make_walker_agents(self):
        unique_id = 0
        while True:
            if unique_id == self.N:
                break
            x = random.randrange(self.grid.width)
            y = random.randrange(self.grid.height)
            pos = (x, y)
            heading = random.choice(self.headings)
            # heading = (1, 0)
            if self.grid.is_cell_empty(pos):
                print("Creating agent {2} at ({0}, {1})"
                      .format(x, y, unique_id))
                a = Walker(unique_id, self, pos, heading)
                self.schedule.add(a)
                self.grid.place_agent(a, pos)
                unique_id += 1

    def step(self):
        self.schedule.step()
Пример #2
0
class ShapeExample(Model):
    def __init__(self, N=2, width=20, height=10):
        self.N = N  # num of agents
        self.headings = ((1, 0), (0, 1), (-1, 0), (0, -1))  # tuples are fast
        self.grid = SingleGrid(width, height, torus=False)
        self.schedule = RandomActivation(self)
        self.make_walker_agents()
        self.running = True

    def make_walker_agents(self):
        unique_id = 0
        while True:
            if unique_id == self.N:
                break
            x = self.random.randrange(self.grid.width)
            y = self.random.randrange(self.grid.height)
            pos = (x, y)
            heading = self.random.choice(self.headings)
            # heading = (1, 0)
            if self.grid.is_cell_empty(pos):
                print("Creating agent {2} at ({0}, {1})".format(
                    x, y, unique_id))
                a = Walker(unique_id, self, pos, heading)
                self.schedule.add(a)
                self.grid.place_agent(a, pos)
                unique_id += 1

    def step(self):
        self.schedule.step()
Пример #3
0
 def getGridStateAtStep(self, step=0):
     plan_agent_keys = [uid for uid, a in self.planAgents.items()]
     perception_agent_keys = [
         uid for uid, a in self.perceptionAgents.items()
     ]
     navGridAtStep = SingleGrid(self.navigationGrid.height,
                                self.navigationGrid.width, False)
     for key in perception_agent_keys:
         navGridAtStep.place_agent(self.perceptionAgents[key],
                                   self.perceptionAgents[key].pos)
     for key in plan_agent_keys:
         for agent in self.planAgents[key]:
             if agent.steps_left == step and navGridAtStep.is_cell_empty(
                     agent.pos):
                 navGridAtStep.place_agent(agent, agent.pos)
     return navGridAtStep
Пример #4
0
class ShapesModel(Model):
    def __init__(self, N, width=20, height=10):
        self.running = True
        self.N = N  # num of agents
        self.headings = ((1, 0), (0, 1), (-1, 0), (0, -1))  # tuples are fast
        self.grid = SingleGrid(width, height, torus=False)
        self.schedule = RandomActivation(self)
        load_scene('shape_model/crossing.txt', self.grid, self)
        """
        self.grid.place_agent(
            Walker(1911, self, (4, 4), type="wall"),
            (4, 4)
        )
        self.make_walls()
        self.make_walker_agents()
        """

    def make_walls(self):
        for i in range(0, 50):
            self.grid.place_agent(Walker(1911, self, (i, 5), type="wall"),
                                  (i, 5))

    def make_walker_agents(self):
        unique_id = 0
        while True:
            if unique_id == self.N:
                break
            x = random.randrange(self.grid.width)
            y = random.randrange(self.grid.height)
            pos = (x, y)
            heading = random.choice(self.headings)
            # heading = (1, 0)
            if self.grid.is_cell_empty(pos):
                print("Creating agent {2} at ({0}, {1})".format(
                    x, y, unique_id))
                a = Walker(unique_id, self, pos, heading)
                self.schedule.add(a)
                self.grid.place_agent(a, (x, y))
                self.grid.place_agent(a, (x + 1, y))
                self.grid.place_agent(a, (x, y + 1))
                self.grid.place_agent(a, (x + 1, y + 1))
                unique_id += 1

    def step(self):
        self.schedule.step()
Пример #5
0
class EvacuationModel(Model):
    """
    This is a simulation of a crowd evacuation from a building.
    Several variables are taken into account: the knowledge of the emergency exits, the age and weight of the agents
    and the presence of stewards that can guide agents toward the emergency exits.
    Agents have different strategies to escape the building such as taking the shortest path to an exit or a random one.

    The goal is to study which combinations of agent types are more likely to escape the building and save themselves and
    how the amount of casualties varies with respect to the different variables.
    """
    def __init__(self,
                 N=10,
                 K=0,
                 width=50,
                 height=50,
                 fire_x=1,
                 fire_y=1,
                 civil_info_exchange=True):
        self.num_civilians = N
        self.num_stewards = K
        self.civil_info_exchange = civil_info_exchange
        self.fire_initial_pos = (fire_x, fire_y)
        self.warning_UI = ""
        self.agents_alive = N + K  # Agents alive and inside the building
        self.agents_saved = []  # Agents that managed to get out
        self.agents_killed = []  # Agents that perished during the evacuation
        self.grid = SingleGrid(height, width, False)
        self.graph = None  # General graph representing walkable terrain
        self.schedule = RandomActivation(
            self)  # Every tick, agents move in a different random order
        # Create exits
        self.pos_exits = [(0, 5), (0, 25), (0, 45)]
        for i in range(3):
            self.pos_exits.append((self.grid.width - 1, 14 + i))

        self.draw_environment(self.pos_exits)
        self.graph = path_finding.create_graph(self)
        # Define data collector
        model_collector = {
            "Agents killed": lambda killed: len(self.agents_killed),
            "Agents saved": lambda saved: len(self.agents_saved)
        }
        for exit_pos in self.pos_exits:
            title = "Exit {}".format(exit_pos)
            model_collector[title] = partial(count_agents_saved, exit_pos)
        self.datacollector = DataCollector(model_reporters=model_collector)
        # Create fire
        # for pos in self.fire_initial_pos:  # Only 1 source of fire since we are setting it from UI
        x, y = self.fire_initial_pos
        if not self.is_inside_square((x, y), (0, 29),
                                     (25, 39)) and not self.is_inside_square(
                                         (x, y), (0, 10), (25, 20)):
            pos = self.fire_initial_pos
        else:
            pos = (1, 1)
            self.warning_UI = "<b>WARNING:</b> Sorry but the position of the fire is outside of the building, " \
                              "change the setting and click reset simulation."
        fire_agent = FireAgent(pos, self)
        self.schedule.add(fire_agent)
        self.grid.place_agent(fire_agent, pos)
        # Create civilian agents
        for i in range(self.num_civilians):

            # a civilian agent will know at least the main entrance to the building
            known_exits = self.pos_exits[-3:]
            a = CivilianAgent(i, self, known_exits)

            self.schedule.add(a)
            # Add the agent to a random grid cell

            while True:
                # pick the random coordinate
                x = self.random.randrange(1, self.grid.width - 1)
                y = self.random.randrange(1, self.grid.height - 1)
                # check if the point is empty and inside of the building
                if self.grid.is_cell_empty((x, y)) and not self.is_inside_square((x, y), (0, 29), (25, 39)) \
                        and not self.is_inside_square((x, y), (0, 10), (25, 20)):
                    break

            self.grid.place_agent(a, (x, y))

        # Create steward agents
        for i in range(self.num_civilians,
                       self.num_civilians + self.num_stewards):

            # a steward agent will know all exits.
            known_exits = self.pos_exits
            a = StewardAgent(i, self, known_exits)

            self.schedule.add(a)
            # Add the agent to a random grid cell

            while True:
                # pick the random coordinate
                x = self.random.randrange(1, self.grid.width - 1)
                y = self.random.randrange(1, self.grid.height - 1)
                # check if the point is empty and inside of the building
                if self.grid.is_cell_empty((x, y)) and not self.is_inside_square((x, y), (0, 29), (25, 39)) \
                        and not self.is_inside_square((x, y), (0, 10), (25, 20)):
                    break

            self.grid.place_agent(a, (x, y))

        self.running = True  # Set this to false when we want to finish simulation (e.g. all agents are out of building)
        self.datacollector.collect(self)

    @staticmethod
    def is_inside_square(point, bottom_left, top_right):
        return bottom_left[0] <= point[0] <= top_right[0] and bottom_left[
            1] <= point[1] <= top_right[1]

    def step(self):
        self.schedule.step()
        # collect data
        self.datacollector.collect(self)

        # Halt if no more agents in the building
        if self.count_agents(self) == 0:
            self.running = False

    def remove_agent(self, agent, reason, **kwargs):
        """
        Removes an agent from the simulation. Depending on the reason it can be
        Args:
            agent (Agent):
            reason (Reasons):

        Returns:
            None
        """
        if reason == Reasons.SAVED:
            self.agents_saved.append(agent)
        elif reason == Reasons.KILLED_BY_FIRE:
            self.agents_killed.append(agent)

        self.agents_alive -= 1
        self.schedule.remove(agent)
        self.grid.remove_agent(agent)

    def draw_environment(self, exits=None):
        length_E = int(self.grid.height /
                       5)  # length of the vertical segments of the E
        depth_E = int(self.grid.width /
                      2)  # length of the horizontal segments of the E
        for i in range(3):
            start = max(0, 2 * i * length_E)
            self.draw_wall((0, start), (0, start + length_E - 1))
        for i in range(2):
            start = 2 * i * length_E + length_E
            self.draw_wall((depth_E, start), (depth_E, start + length_E - 1))
        # Horizontal lines of the E (BB)
        aux_y_coord = [
            length_E, 2 * length_E, 3 * length_E - 1, 4 * length_E - 1
        ]
        for y in aux_y_coord:
            self.draw_wall((0, y), (depth_E, y))
        top_left_corner = (0, self.grid.height - 1)
        top_right_corner = (self.grid.width - 1, self.grid.height - 1)
        bottom_right_corner = (self.grid.width - 1, 0)
        # Draw long contour lines E
        self.draw_wall((0, 0), bottom_right_corner)
        self.draw_wall(top_left_corner, top_right_corner)
        self.draw_wall(bottom_right_corner, top_right_corner)

        # Draw exits
        self.draw_exits(exits)

    def draw_wall(self, start, end):
        """
        Draws a line that goes from start point to end point.

        Args:
            start (List): Coordinates of line's starting point
            end (List): Coordinates of line's end point

        Returns:
            None
        """
        diff_x, diff_y = np.subtract(end, start)
        wall_coordinates = np.asarray(start)

        if self.grid.is_cell_empty(wall_coordinates.tolist()):
            w = WallAgent(wall_coordinates.tolist(), self)
            self.grid.place_agent(w, wall_coordinates.tolist())

        while diff_x != 0 or diff_y != 0:
            if abs(diff_x) == abs(diff_y):
                # diagonal wall
                wall_coordinates[0] += np.sign(diff_x)
                wall_coordinates[1] += np.sign(diff_y)
                diff_x -= 1
                diff_y -= 1
            elif abs(diff_x) < abs(diff_y):
                # wall built in y dimension
                wall_coordinates[1] += np.sign(diff_y)
                diff_y -= 1
            else:
                # wall built in x dimension
                wall_coordinates[0] += np.sign(diff_x)
                diff_x -= 1
            if self.grid.is_cell_empty(wall_coordinates.tolist()):
                w = WallAgent(wall_coordinates.tolist(), self)
                self.grid.place_agent(w, wall_coordinates.tolist())

    def draw_exits(self, exits_list):
        for ext in exits_list:
            e = ExitAgent(ext, self)
            if not self.grid.is_cell_empty(ext):
                # Only walls should exist in the grid at this time, so no need to remove it from scheduler
                agent = self.grid.get_cell_list_contents(ext)
                self.grid.remove_agent(agent[0])
            # Place exit
            self.schedule.add(e)
            self.grid.place_agent(e, ext)

    def spread_fire(self, fire_agent):
        fire_neighbors = self.grid.get_neighborhood(fire_agent.pos,
                                                    moore=True,
                                                    include_center=False)
        for grid_space in fire_neighbors:
            if self.grid.is_cell_empty(grid_space):
                # Create new fire agent and add it to grid and scheduler
                new_fire_agent = FireAgent(grid_space, self)
                self.schedule.add(new_fire_agent)
                self.grid.place_agent(new_fire_agent, grid_space)
            else:
                # If human agents, eliminate them and spread anyway
                agent = self.grid.get_cell_list_contents(grid_space)[0]
                if isinstance(agent, (CivilianAgent, StewardAgent)):
                    new_fire_agent = FireAgent(grid_space, self)
                    self.remove_agent(agent, Reasons.KILLED_BY_FIRE)
                    self.schedule.add(new_fire_agent)
                    self.grid.place_agent(new_fire_agent, grid_space)

    @staticmethod
    def count_agents(model):
        """
        Helper method to count agents alive and still in the building.
        """
        count = 0
        for agent in model.schedule.agents:
            agent_type = type(agent)
            if (agent_type == CivilianAgent) or (agent_type == StewardAgent):
                count += 1
        return count
Пример #6
0
class DaisyModel(Model):
    """ "Daisys" grow, when the temperature is right. But they influence temperature themselves via their ability to block a certain amount of sunlight (albedo, indicated by color). They spread and they mutate (changing albedo) and thus adapt to different conditions."""
    def __init__(self, 
                 N, 
                 width, 
                 height, 
                 luminosity, 
                 heat_radius, 
                 mutation_range, 
                 surface_albedo, 
                 daisy_lifespan, 
                 daisy_tmin, 
                 daisy_tmax,
                 lum_model,
                 lum_increase):
        # Setup parameter
        self.dimensions = (width, height)
        self.running = True # never stop!
        self.num_agents = min([N, (width * height)]) # never more agents than cells
        self.grid = SingleGrid(width, height, torus=True)
        self.schedule = RandomActivation(self)
        # Model parameter
        self.mutation_range = mutation_range # default: 0.05
        self.luminosity = luminosity # default 1.35
        self.heat_radius = heat_radius
        self.surface_albedo = surface_albedo # default: 0.4
        self.lum_model = lum_model
        self.lum_increase = lum_increase # tried 0.001
        # Daisy parameter
        self.daisy_lifespan = daisy_lifespan
        self.daisy_tmin = daisy_tmin
        self.daisy_tmax = daisy_tmax

        # to inhibit using same postition twice: draw from urn
        position_list = []
        for i in range(width): # put positions in urn
            for j in range(height):
                position_list.append((i,j))
        for i in range(self.num_agents): # draw from urn
            a = DaisyAgent(i, self, 
                            random.uniform(0.1, 0.9), # random starting albedo
                            self.daisy_lifespan, self.daisy_tmin, self.daisy_tmax)
            self.schedule.add(a)
            pos = random.choice(position_list)
            self.grid.place_agent(a, pos)
            position_list.remove(pos)

        # Data collectors
        self.datacollector = DataCollector(
            model_reporters = {"Solar irradiance": get_irradiance, 
                               "Population": get_population,
                               "Mean albedo": get_mean_albedo,
                               "Population: North - South": get_north_south_population
                               }
        )

    def step(self):
        print(self.lum_model)
        if self.lum_model == 'linear increase':
            self.luminosity = linear_increase(self)


        self.datacollector.collect(self)
        self.schedule.step()
        

    def get_lat(self, pos):
        """ The grid is meant to be a sphere. This gets the latitude. Ranges from 0.0 (equator) to 1.0 (pole).  """
        return (pos[1] / self.dimensions[1])

    def get_GNI(self, pos):
        """ gives solar irradiance, depending on latitude"""
        return self.luminosity * math.sin(self.get_lat(pos)*math.pi)

    def expand_positionlist(self, pos_list):
        """ expands a list of positions, adding neighboring positions  """
        expanded_list = []
        for i in pos_list:
            expanded_list += self.grid.get_neighborhood(i, moore=True, include_center=False)
        return list(set(expanded_list))

    def get_local_heat(self, pos):
        """ Global Horizontal Irradiance (without diffusive irradiance) from pole (lower border) to pole (upper border). model is torus! """
        neighborhood = self.grid.get_neighborhood(pos, moore=True, include_center=True)
        
        if self.heat_radius > 1: # if radius of local temperature is >1, this expand the position list.
            for i in range(self.heat_radius):
                neighborhood = self.expand_positionlist(neighborhood)

        heat = []
        for i in neighborhood:
            if self.grid.is_cell_empty(i): # empty cell: surface albedo
                heat.append(self.get_GNI(pos) * (1 - self.surface_albedo) )
            else:
                inhabitant = self.grid.get_cell_list_contents(i)[0] 
                heat.append(self.get_GNI(pos) * (1 - inhabitant.albedo) ) # cell with daisy
        return sum(heat)/ len(neighborhood)
Пример #7
0
class AgentKnowledgeMap():
    '''
    *** Constructor:
        Inputs:
               - height and width of the grid used by the AgSimulator

       Actions:
               - Construct navigationGrid
               - Construct planGrid
               - Create agent dictionaries
    '''
    def __init__(self, height, width, model):
        self.navigationGrid = SingleGrid(height, width, False)
        self.planGrid = MultiGrid(height, width, False)
        self.planAgents = defaultdict(list)
        self.perceptionAgents = {}
        self.model = model
        agent = FarmAgent(0, self.model.farmPos, self)
        self.navigationGrid.place_agent(agent, self.model.farmPos)
        self.attendancePoints = list()

    '''
    *** update function is used by each ActiveAgent to update ActiveAgentKnowledgeMap
        Input:
              - ActiveAgentPlanning objects are placed on planGrid
              - PassiveAgentPerception objects are placed on navigationGrid
    '''

    def update(self, agent):
        if (isinstance(agent, ActiveAgentPlanning)):
            self.planGrid.place_agent(agent, agent.pos)
            self.planAgents.setdefault(agent.unique_id, [])
            self.planAgents[agent.unique_id].append(agent)
        elif (isinstance(agent, PassiveAgentPerception)):
            if self.navigationGrid.is_cell_empty(agent.pos):
                self.navigationGrid.place_agent(agent, agent.pos)
                self.perceptionAgents[agent.unique_id] = agent
            else:
                existing_agent = self.navigationGrid.get_cell_list_contents(
                    agent.pos)[0]
                existing_agent.update(agent.state, agent.time_at_current_state)

    # This function is used for removing a step from the KnowledgeMap
    def removeOneStep(self, agentID):
        if self.planAgents[agentID]:
            self.planGrid.remove_agent(self.planAgents[agentID].pop(0))

    # This function is used for canceling the entire plan in case a collision is detected
    def cancelPlan(self, agentID):
        while len(self.planAgents[agentID]) > 0:
            self.planGrid.remove_agent(self.planAgents[agentID].pop(0))

    '''
    *** getGridStateAtStep returns a SingleGrid object with anticipated state of the grid at specified steps
        Input:
              - step for which the SingleGrid should be generated
        Output:
              - SingleGrid object with PassiveAgentPerception objects and ActiveAgentPlanning objects corresponding to chosen step
    '''

    def getGridStateAtStep(self, step=0):
        plan_agent_keys = [uid for uid, a in self.planAgents.items()]
        perception_agent_keys = [
            uid for uid, a in self.perceptionAgents.items()
        ]
        navGridAtStep = SingleGrid(self.navigationGrid.height,
                                   self.navigationGrid.width, False)
        for key in perception_agent_keys:
            navGridAtStep.place_agent(self.perceptionAgents[key],
                                      self.perceptionAgents[key].pos)
        for key in plan_agent_keys:
            for agent in self.planAgents[key]:
                if agent.steps_left == step and navGridAtStep.is_cell_empty(
                        agent.pos):
                    navGridAtStep.place_agent(agent, agent.pos)
        return navGridAtStep

    # This function is used to get a numpy array containing 0 and 1;
    # 0 for empty blocks at step X
    # 1 for any kind of agent at step X
    def getGridAtStepAsNumpyArray(self, step=0):
        plan_agent_keys = [uid for uid, a in self.planAgents.items()]
        perception_agent_keys = [
            uid for uid, a in self.perceptionAgents.items()
        ]
        return_numpy_array = numpy.zeros(
            (self.navigationGrid.width, self.navigationGrid.height),
            dtype='int8')
        for key in perception_agent_keys:
            return_numpy_array[self.perceptionAgents[key].pos[1],
                               self.perceptionAgents[key].pos[0]] = 1
        for agent_key in self.planAgents:
            agent_plans = self.planAgents[agent_key]
            if len(agent_plans) > 0 and len(agent_plans) >= step:
                for plan in agent_plans:
                    if plan.steps_left == step:
                        return_numpy_array[plan.pos[1], plan.pos[0]] = 1
            elif len(agent_plans) == 0:
                active_agent = self.model.schedule.getPassiveAgent(agent_key)
                return_numpy_array[active_agent.pos[1],
                                   active_agent.pos[0]] = 1
            else:
                return_numpy_array[agent_plans[-1].pos[1],
                                   agent_plans[-1].pos[0]] = 1
        return_numpy_array[self.model.farmPos[1], self.model.farmPos[0]] = 1
        return return_numpy_array
Пример #8
0
class GTModel(Model):
    def __init__(self, debug, size, i_n_agents, i_strategy, i_energy,
                 child_location, movement, k, T, M, p, d, strategies_to_count,
                 count_tolerance, mutation_type, death_threshold, n_groups):
        self.grid = SingleGrid(size, size, torus=True)
        self.schedule = RandomActivation(self)
        self.running = True
        self.debug = debug
        self.size = size
        self.agent_idx = 0
        self.i_energy = i_energy

        # Payoff matrix in the form (my_move, op_move) : my_reward
        self.payoff = {
            ('C', 'C'): 2,
            ('C', 'D'): -3,
            ('D', 'C'): 3,
            ('D', 'D'): -1,
        }
        # Constant for max population control (cost of surviving)
        self.k = k
        # Constant for controlling dying of old age
        self.M = M
        # Minimum lifespan
        self.T = T
        # Minimum energy level to reproduce
        self.p = p
        # Mutation "amplitude"
        self.d = d
        # Whether to spawn children near parents or randomly
        self.child_location = child_location
        # Specify the type of movement allowed for the agents
        self.movement = movement
        # Specify how the agents mutate
        self.mutation_type = mutation_type
        # The minimum total_energy needed for an agent to survive
        self.death_threshold = death_threshold

        # Vars regarding which strategies to look for
        self.strategies_to_count = strategies_to_count
        self.count_tolerance = count_tolerance

        # Add agents (one agent per cell)
        all_coords = [(x, y) for x in range(size) for y in range(size)]
        agent_coords = self.random.sample(all_coords, i_n_agents)

        for _ in range(i_n_agents):
            group_idx = (None if n_groups is None else self.random.choice(
                range(n_groups)))
            agent = GTAgent(self.agent_idx, group_idx, self, i_strategy.copy(),
                            i_energy)
            self.agent_idx += 1
            self.schedule.add(agent)
            self.grid.place_agent(agent, agent_coords.pop())

        # Collect data
        self.datacollector = DataCollector(
            model_reporters={
                **{
                    'strategies': get_strategies,
                    'n_agents': total_n_agents,
                    'avg_agent_age': avg_agent_age,
                    'n_friendlier': n_friendlier,
                    'n_aggressive': n_aggressive,
                    'perc_cooperative_actions': perc_cooperative_actions,
                    'n_neighbors': n_neighbor_measure,
                    'avg_delta_energy': avg_delta_energy,
                    'perc_CC': perc_CC_interactions,
                    'lin_fit_NC': coop_per_neig,
                    'lin_fit_NC_intc': coop_per_neig_intc,
                },
                **{
                    label: strategy_counter_factory(strategy, count_tolerance)
                    for label, strategy in strategies_to_count.items()
                }
            })

    def alpha(self):
        # Return the cost of surviving, alpha
        DC = self.payoff[('D', 'C')]
        CC = self.payoff[('C', 'C')]
        N = len(self.schedule.agents)

        return self.k + 4 * (DC + CC) * N / (self.size * self.size)

    def time_to_die(self, agent):
        # There is a chance every iteration to die of old age: (A - T) / M
        # There is a 100% to die if the agents total energy reaches 0
        return (agent.total_energy < self.death_threshold
                or self.random.random() < (agent.age - self.T) / self.M)

    def get_child_location(self, agent):
        if self.child_location == 'global':
            return self.random.choice(sorted(self.grid.empties))

        elif self.child_location == 'local':
            # Iterate over the radius, starting at 1 to find empty cells
            for rad in range(1, int(self.size / 2)):
                possible_steps = [
                    cell for cell in self.grid.get_neighborhood(
                        agent.pos,
                        moore=False,
                        include_center=False,
                        radius=rad,
                    ) if self.grid.is_cell_empty(cell)
                ]

                if possible_steps:
                    return self.random.choice(possible_steps)

            # If no free cells in radius size/2 pick a random empty cell
            return self.random.choice(sorted(self.grid.empties))

    def maybe_mutate(self, agent):
        # Mutate by adding a random d to individual Pi's
        if self.mutation_type == 'stochastic':
            # Copy the damn list
            new_strategy = agent.strategy.copy()
            # There is a 20% chance of mutation
            if self.random.random() < 0.2:
                # Each Pi is mutated uniformly by [-d, d]
                for i in range(4):
                    mutation = self.random.uniform(-self.d, self.d)
                    new_val = new_strategy[i] + mutation
                    # Keep probabilities in [0, 1]
                    new_val = (0 if new_val < 0 else
                               1 if new_val > 1 else new_val)
                    new_strategy[i] = new_val

        # Mutate by choosing a random strategy from the list set
        elif self.mutation_type == 'fixed':
            new_strategy = random.choice(
                list(self.strategies_to_count.values()))

        elif self.mutation_type == 'gaussian_sentimental':
            # Copy the damn list
            new_strategy = agent.strategy.copy()
            # There is a 20% chance of mutation
            if self.random.random() < 0.2:
                # Each Pi is mutated by a value drawn from a gaussian
                # with mean=delta_energy
                for i in range(4):
                    mutation = self.random.normalvariate(
                        (agent.delta_energy + self.alpha()) / 14, self.d)
                    new_val = new_strategy[i] + mutation
                    # Keep probabilities in [0, 1]
                    new_val = (0 if new_val < 0 else
                               1 if new_val > 1 else new_val)
                    new_strategy[i] = new_val

        return new_strategy

    def maybe_reproduce(self, agent):
        # If we have the energy to reproduce, do so
        if agent.total_energy >= self.p:
            # Create the child
            new_strategy = self.maybe_mutate(agent)
            child = GTAgent(self.agent_idx, agent.group_id, self, new_strategy,
                            self.i_energy)
            self.agent_idx += 1

            # Set parent and child energy levels to p/2
            child.total_energy = self.p / 2
            agent.total_energy = self.p / 2

            # Place child (Remove agent argument for global child placement)
            self.schedule.add(child)
            self.grid.place_agent(child, self.get_child_location(agent))

    def step(self):
        if self.debug:
            print('\n\n==================================================')
            print('==================================================')
            print('==================================================')
            pprint(vars(self))

        # First collect data
        self.datacollector.collect(self)

        # Then check for dead agents and for new agents
        for agent in self.schedule.agent_buffer(shuffled=True):
            # First check if dead
            if self.time_to_die(agent):
                self.grid.remove_agent(agent)
                self.schedule.remove(agent)

            # Otherwise check if can reproduce
            else:
                self.maybe_reproduce(agent)

        # Finally, step each agent
        self.schedule.step()

    def check_strategy(self, agent):
        # Helper function to check which strategy an agent would count as
        def is_same(strategy, a_strategy):
            tol = self.count_tolerance
            return all(strategy[i] - tol < a_strategy[i] < strategy[i] + tol
                       for i in range(4))

        return [
            name for name, strat in self.strategies_to_count.items()
            if is_same(strat, agent.strategy)
        ]
Пример #9
0
class DiseaseModel(Model):
    """
    A model with some number of agents.
    highS: Number of agents with high sociability.
    middleS: Number of agents with middle sociability.
    lowS: Number of agents with low sociability.
    width: Width of the grid.
    height: Height of the grid.
    edu_setting: If true, agents will follow a schedule and sit in classrooms,
    else they will move freely through an open grid.
    cureProb: Probability of agent getting better.
    cureProbFac: Factor of cureProb getting higher.
    mutateProb: Probability of a disease mutating.
    diseaseRate: Rate at which the disease spreads.
    """
    def __init__(self, highS, middleS, lowS, width, height, edu_setting=True,
                 cureProb=0.1, cureProbFac=2/1440, mutateProb=0.0050,
                 diseaseRate=0.38):
        super().__init__()
        self.num_agents = highS + middleS + lowS
        self.lowS = lowS
        self.middleS = middleS
        self.highS = highS
        self.initialCureProb = cureProb
        self.cureProbFac = cureProbFac
        self.mutateProb = mutateProb
        self.diseaseRate = diseaseRate
        self.edu_setting = edu_setting
        self.maxDisease = 0  # amount of mutations
        self.counter = 540  # keeps track of timesteps
        self.removed = []
        self.exit = (width - 1, floor(height / 2))
        # Check if agents fit within grid
        if self.num_agents > width * height:
            raise ValueError("Number of agents exceeds grid capacity.")

        # Create grid with random activation
        self.grid = SingleGrid(width, height, True)
        self.schedule = RandomActivation(self)

        if edu_setting:
            # Create walls
            numberRooms = 3
            self.add_walls(numberRooms, width, height)

            self.midWidthRoom = floor(width / numberRooms / 2)
            self.midHeightRoom = floor(height / numberRooms / 2)
            self.widthRoom = floor(width / numberRooms)
            self.heightRoom = floor(height / numberRooms)
            numberRows = floor((self.heightRoom) / 2)
            widthRows = self.widthRoom - 4
            location = [[] for _ in range(numberRooms * 2)]
            for i in range(numberRooms):
                for j in range(0, numberRows, 2):
                    startWidth = 2 + (i % 3) * self.widthRoom
                    for currentWidth in range(widthRows):
                        location[i] += [(startWidth + currentWidth, j)]
            for i in range(3, numberRooms * 2):
                for j in range(0, numberRows, 2):
                    startWidth = 2 + (i % 3) * self.widthRoom
                    for currentWidth in range(widthRows):
                        location[i] += [(startWidth + currentWidth,
                                         height - 1 - j)]

            # Set 3 goals per roster
            self.roster = [[location[0], location[3], location[1]],
                           [location[5], location[2], location[0]],
                           [location[4], location[1], location[5]]]

        # Create agents
        self.addAgents(lowS, 0, 0)
        self.addAgents(middleS, lowS, 1)
        self.addAgents(highS, lowS + highS, 2)

        # set up data collecter
        self.datacollector = DataCollector(
            model_reporters={"diseasepercentage": disease_collector},
            agent_reporters={"disease": "disease"})

    def heuristic(self, start, goal):
        """
        Returns manhattan distance.
        start: current location (x,y)
        goal: goal location (x,y)
        """
        dx = abs(start[0] - goal[0])
        dy = abs(start[1] - goal[1])
        return dx + dy

    def get_vertex_neighbors(self, pos):
        """
        Returns all neighbors.
        pos: current position
        """
        n = self.grid.get_neighborhood(pos, moore=False)
        neighbors = []
        for item in n:
            if not abs(item[0] - pos[0]) > 1 and not abs(item[1] - pos[1]) > 1:
                neighbors += [item]
        return neighbors

    def move_cost(self, location):
        """
        Return the cost of a location.
        """
        if self.grid.is_cell_empty(location):
            return 1  # Normal movement cost
        else:
            return 100  # Very difficult to go through walls

    def add_walls(self, n, widthGrid, heightGrid):
        """
        Add walls in grid.
        n: number of rooms horizontally
        widthGrid: width of the grid
        heightGrid: height of the grid
        """
        widthRooms = floor(widthGrid / n)
        heightRooms = floor(heightGrid / n)
        heightHall = heightGrid - 2 * heightRooms
        # Add horizontal walls
        for i in range(n - 1):
            for y in range(heightRooms):
                brick = wall(self.num_agents, self)
                self.grid.place_agent(brick, ((i + 1) * widthRooms, y))
                self.grid.place_agent(brick, ((i + 1) * widthRooms, y +
                                      heightRooms + heightHall))
        doorWidth = 2
        # Add vertical walls
        for x in range(widthGrid):
            if (x % widthRooms) < (widthRooms - doorWidth):
                brick = wall(self.num_agents, self)
                self.grid.place_agent(brick, (x, heightRooms))
                self.grid.place_agent(brick, (x, heightRooms + heightHall - 1))

    def addAgents(self, n, startID, sociability):
        """
        Add agents with a sociability.
        n: number of agents
        startID: ID of the first added agent
        sociability: sociability of the agents
        """
        disease_list = np.random.randint(0, 2, n)
        for i in range(n):
            # Set schedule for every agent if educational setting
            if self.edu_setting:
                a_roster = []
                rosterNumber = self.random.randrange(len(self.roster))
                rooms = self.roster[rosterNumber]
                for roomNumber in range(len(rooms)):
                    loc = self.random.choice(rooms[roomNumber])
                    a_roster += [loc]
                    (self.roster[rosterNumber][roomNumber]).remove(loc)
            else:
                a_roster = []

            a = DiseaseAgent(i + startID, sociability, self, disease_list[i],
                             a_roster)
            self.schedule.add(a)
            # Set agent outside grid, ready to enter, if edu setting
            # else randomly place on empty spot on grid
            if self.edu_setting:
                self.removed += [a]
                a.pos = None
            else:
                self.grid.place_agent(a, self.grid.find_empty())

    def step(self):
        """
        Continue one step in simulation.
        """
        self.counter += 1
        self.datacollector.collect(self)
        self.schedule.step()
Пример #10
0
class Anthill(Model):
    def __init__(self):

        self.grid = SingleGrid(WIDTH, HEIGHT, False)
        self.schedule = RandomActivation(self)
        self.running = True
        self.internalrate = 0.2
        self.ant_id = 1
        self.tau = np.zeros((WIDTH, HEIGHT))
        self.datacollector = DataCollector({
            "Total number of Ants":
            lambda m: self.get_total_ants_number(),
            "mean tau":
            lambda m: self.evaluation1(),
            "sigma":
            lambda m: self.evaluation2(),
            "sigma*":
            lambda m: self.evaluation3(),
        })

        # List containing all coordinates of the boundary, initial ants location and brood location
        self.bound_vals = []
        self.neigh_bound = []
        self.datacollector.collect(self)

        for i in range(WIDTH):
            for j in range(HEIGHT):
                if i == 0 or j == 0 or i == WIDTH - 1 or j == HEIGHT - 1:
                    self.bound_vals.append((i, j))
                if i == 1 or i == WIDTH - 2 or j == 1 or j == HEIGHT - 2:
                    self.neigh_bound.append((i, j))

        # Make a Fence boundary
        b = 0
        for h in self.bound_vals:
            br = Fence(b, self)

            self.grid.place_agent(br, (h[0], h[1]))
            b += 1

    def step(self):
        '''Advance the model by one step.'''
        # Add new ants into the internal area ont he boundary

        for xy in self.neigh_bound:

            # Add with probability internal rate and if the cell is empty
            if self.random.uniform(
                    0, 1) < self.internalrate and self.grid.is_cell_empty(
                        xy) == True:

                a = Ant(self.ant_id, self)

                self.schedule.add(a)
                self.grid.place_agent(a, xy)

                self.ant_id += 1

        # Move the ants
        self.schedule.step()
        self.datacollector.collect(self)

        # Remove all ants on bounary

        for (agents, i, j) in self.grid.coord_iter():
            if (i, j) in self.neigh_bound and type(agents) is Ant:

                self.grid.remove_agent(agents)
                self.schedule.remove(agents)

        data_tau.append(self.mean_tau_ant)
        data_sigma.append(np.sqrt(self.sigma))
        data_sigmastar.append(self.sigmastar)

        if len(data_sigmastar) > 20:
            if abs(data_sigmastar[-2] - data_sigmastar[-1]) < 0.0000001 or len(
                    data_sigmastar) == 2000:
                try:
                    # TAU
                    with open("results/m1_tau_5.pkl", 'rb') as f:
                        tau_old = pickle.load(f)
                        tau_old[int(len(tau_old) + 1)] = data_tau
                        f.close()
                    pickle.dump(tau_old, open("results/m1_tau_5.pkl", 'wb'))

                except:
                    pickle.dump({1: data_tau},
                                open("results/m1_tau_5.pkl", 'wb'))

                try:
                    # SIGMA
                    with open("results/m1_sigma_5.pkl", 'rb') as f:
                        sigma_old = pickle.load(f)
                        sigma_old[int(len(sigma_old) + 1)] = data_sigma
                        f.close()
                    pickle.dump(sigma_old, open("results/m1_sigma_5.pkl",
                                                'wb'))

                except:
                    pickle.dump({1: data_sigma},
                                open("results/m1_sigma_5.pkl", 'wb'))

                try:
                    # SIGMASTAR
                    with open("results/m1_sigmastar_5.pkl", 'rb') as f:
                        sigmastar_old = pickle.load(f)
                        sigmastar_old[int(len(sigmastar_old) +
                                          1)] = data_sigmastar
                        f.close()
                    pickle.dump(sigmastar_old,
                                open("results/m1_sigmastar_5.pkl", 'wb'))

                except:
                    pickle.dump({1: data_sigmastar},
                                open("results/m1_sigmastar_5.pkl", 'wb'))

                try:
                    # MATRIX
                    with open("results/m1_matrix_5.pkl", 'rb') as f:
                        matrix_old = pickle.load(f)
                        matrix_old[int(len(matrix_old) + 1)] = self.tau
                        f.close()
                    pickle.dump(matrix_old,
                                open("results/m1_matrix_5.pkl", 'wb'))

                except:
                    pickle.dump({1: self.tau},
                                open("results/m1_matrix_5.pkl", 'wb'))
                print(
                    "_______________________________________________________________________"
                )
                print("DONE")
                self.running = False

        # with open("tau2_new.txt", "a") as myfile:
        #     myfile.write(str(self.mean_tau_ant) + '\n')
        # with open("sigma2_new.txt", "a") as myfile:
        #     myfile.write(str(np.sqrt(self.sigma)) + '\n')
        # with open("datasigmastar2_new.txt","a") as myfile:
        #     myfile.write(str(self.sigmastar) + "\n")

    def get_total_ants_number(self):
        total_ants = 0
        for (agents, _, _) in self.grid.coord_iter():
            if type(agents) is Ant:
                total_ants += 1
        return total_ants

    def evaluation1(self):

        ##creat a empty grid to store currently information
        total_ants = np.zeros((WIDTH, HEIGHT))

        ## count the number of currently information
        for (agents, i, j) in self.grid.coord_iter():

            if type(agents) is Ant:
                total_ants[i][j] = 1
            else:
                total_ants[i][j] = 0

        ##update the tau
        self.tau = self.tau + total_ants

        ##calcualte the mean tau
        self.mean_tau_ant = self.tau.sum() / ((WIDTH - 2)**2)

        return self.mean_tau_ant

    def evaluation2(self):

        ## we need to minus the mean tau so we need to ensure the result of boundary is zero
        ## so we let the bounday equal mean_tau_ant in this way the (tau-mean_tau_ant) is zero of boundary
        for site in self.bound_vals:
            self.tau[site[0]][site[1]] = self.mean_tau_ant

        ## calculate the sigmaa
        self.sigma = ((self.tau - self.mean_tau_ant)**2).sum() / (
            (WIDTH - 2)**2)

        ## rechange the boundaryy
        for site in self.bound_vals:
            self.tau[site[0]][site[1]] = 0

        return np.sqrt(self.sigma)

    def evaluation3(self):
        ## calculate the sigmastar
        self.sigmastar = np.sqrt(self.sigma) / self.mean_tau_ant

        return self.sigmastar
class modelSim(Model):
    """ 
    details of the world 
    
    introduce time is when animal agents first get introduced into the wrold
    disp_rate is the dispersal rate for experiment 3
    dist is perceptual strength for animals if fixed
    det is decision determinacy of animals if fixed
    cog_fixed determines if cognition of animals is fixed to particular values or is allowed to evolve
    if skip_300 is True, patchiness values are not calculated for the first 300 steps-- this makes the model run faster
    collect_cog_dist creates a seperate dataframe for all cognition values for agents at every timestep
    if evolve_disp is true, dispersion rate of plants is free to evolve
    """

    def __init__(self, introduce_time, disp_rate, dist, det, cog_fixed = False, \
                 skip_300 = True, collect_cog_dist = False, evolve_disp = False):

        self.skip_300 = skip_300
        self.cog_fixed = cog_fixed
        self.evolve_disp = evolve_disp
        self.collect_cog_dist = collect_cog_dist
        self.dist = dist
        self.det = det
        self.disp_rate = disp_rate
        self.intro_time = introduce_time
        (self.a1num, self.a2num) = (20, 20)
        self.schedule = RandomActivation(
            self)  # agents take a step in random order
        self.grid = SingleGrid(
            200, 200,
            True)  # the world is a grid with specified height and width

        self.initialize_perception()

        disp = np.power(self.disp_rate, range(0, 100))
        self.disp = disp / sum(disp)
        self.grid_ind = np.indices((200, 200))
        positions = np.maximum(abs(100 - self.grid_ind[0]),
                               abs(100 - self.grid_ind[1]))
        self.positions = np.minimum(positions, 200 - positions)

        self.agentgrid = np.zeros(
            (self.grid.width, self.grid.height
             ))  # allows for calculation of patchiness of both agents
        self.coggrid = np.full(
            (self.nCogPar, self.grid.width, self.grid.height), 101.0)
        self.dispgrid = np.full((2, self.grid.width, self.grid.height), 101.0)
        self.age = []
        (self.nstep, self.unique_id, self.reprod, self.food, self.death,
         self.combat) = (0, 0, 0, 0, 0, 0)

        self.cmap = colors.ListedColormap([
            'midnightblue', 'mediumseagreen', 'white', 'white', 'white',
            'white', 'white'
        ])  #'yellow', 'orange', 'red', 'brown'])
        bounds = [0, 1, 2, 3, 4, 5, 6, 7]
        self.norm = colors.BoundaryNorm(bounds, self.cmap.N)

        self.expect_NN = []
        self.NN = [5, 10]
        for i in self.NN:
            self.expect_NN.append(
                (math.factorial(2 * i) * i) / (2**i * math.factorial(i))**2)

        grid_ind_food = np.indices((21, 21))
        positions_food = np.maximum(abs(10 - grid_ind_food[0]),
                                    abs(10 - grid_ind_food[1]))
        self.positions_food = np.minimum(positions_food, 21 - positions_food)
        if self.collect_cog_dist:
            self.cog_dist_dist = pd.DataFrame(columns=[])
            self.cog_dist_det = pd.DataFrame(columns=[])

        for i in range(self.a1num):  # initiate a1 agents at random locations
            self.introduce_agents("A1")
        self.nA1 = self.a1num
        self.nA2 = 0
#     self.agent_steps = {}

    def initialize_perception(self):
        self.history = pd.DataFrame(columns=[
            "nA1", "nA2", "age", "LIP5", "LIP10", "LIPanim5", "LIPanim10",
            "Morsita5", "Morsita10", "Morsitaanim5", "Morsitaanim10", "NN5",
            "NN10", "NNanim5", "NNanim10", "reprod", "food", "death", "combat",
            "dist", "det", "dist_lower", "det_lower", "dist_upper",
            "det_upper", "dist_ci", "det_ci"
        ])
        self.nCogPar = 2
        (self.start_energy, self.eat_energy, self.tire_energy, self.reproduction_energy, self.cognition_energy) \
        = (10, 5, 3, 20, 1)

    def introduce_agents(self, which_agent):
        x = random.randrange(self.grid.width)
        y = random.randrange(self.grid.height)

        if which_agent == "A1":
            if self.grid.is_cell_empty((x, y)):
                a = A1(self.unique_id, self, self.start_energy, disp_rate=0)
                self.unique_id += 1
                self.grid.position_agent(a, x, y)
                self.schedule.add(a)
                self.agentgrid[x][y] = 1
            else:
                self.introduce_agents(which_agent)
        elif which_agent == "A2":
            if self.cog_fixed:
                c = (self.dist, self.det)
            else:
                c = tuple([0] * self.nCogPar)
            a = A2(self.unique_id,
                   self,
                   self.start_energy,
                   cognition=c,
                   disp_rate=0)
            self.unique_id += 1
            if self.agentgrid[x][y] == 1:
                die = self.grid.get_cell_list_contents([(x, y)])[0]
                die.dead = True
                self.grid.remove_agent(die)
                self.schedule.remove(die)
                self.grid.place_agent(a, (x, y))
                self.schedule.add(a)
                self.agentgrid[x][y] = 2
                self.coggrid[:, x, y] = c
            elif self.agentgrid[x][y] == 0:
                self.grid.place_agent(a, (x, y))
                self.schedule.add(a)
                self.agentgrid[x][y] = 2
                self.coggrid[:, x, y] = c

    def flatten_(self, n, grid, full_grid=False, mean=True, range_=False):
        if full_grid:
            return (grid[n].flatten())
        i = grid[n].flatten()
        if mean:
            i = np.delete(i, np.where(i == 101))
            if len(i) == 0:
                # if range_:
                return ([0] * 4)
            #else:
            #    return(0)
            if range_:
                if self.cog_fixed:
                    return ([np.mean(i)] * 4)
                return (np.concatenate(
                    ([np.mean(i)], np.percentile(i, [2.5, 97.5]),
                     self.calculate_ci(i))))
            return ([np.mean(i), 0, 0, 0])
        else:
            return (i)

    def calculate_ci(self, data):
        if np.min(data) == np.max(data):
            return ([0.0])
        return ([
            np.mean(data) - st.t.interval(
                0.95, len(data) - 1, loc=np.mean(data), scale=st.sem(data))[0]
        ])

    def return_zero(self, num, denom):
        if self.nstep == 1:
            #     print("whaaat")
            return (0)
        if denom == "old_nA2":
            denom = self.history["nA2"][self.nstep - 2]
        if denom == 0.0:
            return 0
        return (num / denom)

    def nearest_neighbor(self, agent):  # fix this later
        if agent == "a1":
            x = np.argwhere(self.agentgrid == 1)
            if len(x) <= 10:
                return ([-1] * len(self.NN))
            elif len(x) > 39990:
                return ([0.97, 0.99])
        #  if self.nstep<300 and self.skip_300:
        #      return([-1,-1] )
        else:
            x = np.argwhere(self.agentgrid == 2)
            if len(x) <= 10:
                return ([-1] * len(self.NN))
        density = len(x) / (self.grid.width)**2
        expect_NN_ = self.expect_NN
        expect_dist = np.array(expect_NN_) / (density**0.5)
        distances = [0, 0]
        for i in x:
            distx = abs(x[:, 0] - i[0])
            distx[distx > 100] = 200 - distx[distx > 100]
            disty = abs(x[:, 1] - i[1])
            disty[disty > 100] = 200 - disty[disty > 100]
            dist = (distx**2 + disty**2)**0.5
            distances[0] += (np.partition(dist, 5)[5])
            distances[1] += (np.partition(dist, 10)[10])
        mean_dist = np.array(distances) / len(x)
        out = mean_dist / expect_dist
        return (out)

    def quadrant_patch(
        self, agent
    ):  # function to calculate the patchiness index of agents at every step
        if agent == "a1":
            x = self.agentgrid == 1
        else:
            x = self.agentgrid == 2
        gsize = np.array([5, 10])
        gnum = 200 / gsize
        qcs = []
        for i in range(2):
            x_ = x.reshape(int(gnum[i]), gsize[i], int(gnum[i]),
                           gsize[i]).sum(1).sum(2)
            mean = np.mean(x_)
            var = np.var(x_)
            if mean == 0.0:
                return ([-1] * 4)
            lip = 1 + (var - mean) / (mean**2)
            morsita = np.sum(x) * ((np.sum(np.power(x_, 2)) - np.sum(x_)) /
                                   (np.sum(x_)**2 - np.sum(x_)))
            qcs += [lip, morsita]
        return (qcs)

    def l_function(self, agent):
        if agent == "a1":
            x = np.argwhere(self.agentgrid == 1)
        else:
            x = np.argwhere(self.agentgrid == 2)
            if len(x) == 0:
                return (-1)
        distances = np.array([])
        for i in x:
            distx = abs(x[:, 0] - i[0])
            distx[distx > 100] = 200 - distx[distx > 100]
            disty = abs(x[:, 1] - i[1])
            disty[disty > 100] = 200 - disty[disty > 100]
            dist = (distx**2 + disty**2)**0.5
            distances = np.concatenate((distances, dist[dist != 0]))
        l = np.array([])
        for i in np.arange(5, 51, 5):
            l = np.append(l, sum(distances < i))
        k = (l * 200**2) / (len(x)**2)
        l = (k / math.pi)**0.5
        return (abs(l - np.arange(5, 51, 5)))

    def collect_hist(self):
        if self.nstep < 300 and self.skip_300:
            NNcalc = [-1, -1]  #self.nearest_neighbor("a1")
            NNanimcalc = [-1, -1]  #self.nearest_neighbor("a2")
        else:
            NNcalc = self.nearest_neighbor("a1")
            NNanimcalc = self.nearest_neighbor("a2")
        quadrantcalc = self.quadrant_patch("a1")
        quadrantanimcalc = self.quadrant_patch("a2")
        dist_values = self.flatten_(0,
                                    grid=self.coggrid,
                                    mean=True,
                                    range_=False)
        det_values = self.flatten_(1,
                                   grid=self.coggrid,
                                   mean=True,
                                   range_=False)
        # l_f = 0#self.l_function("a1")
        dat = {
            "nA1": self.nA1,
            "nA2": self.nA2,
            "age": self.return_zero(sum(self.age), self.nA2),
            "LIP5": quadrantcalc[0],
            "LIP10": quadrantcalc[2],
            "LIPanim5": quadrantanimcalc[0],
            "LIPanim10": quadrantanimcalc[2],
            "Morsita5": quadrantcalc[1],
            "Morsita10": quadrantcalc[3],
            "Morsitaanim5": quadrantanimcalc[1],
            "Morsitaanim10": quadrantanimcalc[3],
            "NN5": NNcalc[0],
            "NN10": NNcalc[1],
            "NNanim5": NNanimcalc[0],
            "NNanim10":
            NNanimcalc[1],  #"l_ripley" : l_f,# self.nearest_neighbor("a2"),  
            "reprod": self.return_zero(self.reprod, "old_nA2"),
            "food": self.return_zero(self.food, self.nA2),
            "death": self.return_zero(self.death, "old_nA2"),
            "combat": self.return_zero(self.combat, "old_nA2"),
            "dist": dist_values[0],
            "det": det_values[0],
            "dist_lower": dist_values[1],
            "det_lower": det_values[1],
            "dist_upper": dist_values[2],
            "det_upper": det_values[2],
            "dist_ci": dist_values[3],
            "det_ci": det_values[3],
            "disp_a1": self.flatten_(0, grid=self.dispgrid)[0],
            "disp_a2": self.flatten_(1, grid=self.dispgrid)[0]
        }
        self.history = self.history.append(dat, ignore_index=True)
        self.age = []
        (self.reprod, self.food, self.death, self.combat) = (0, 0, 0, 0)
        if self.collect_cog_dist:
            if (self.nstep % 10) == 0:
                self.cog_dist_dist[str(self.nstep - 1)] = self.flatten_(
                    0, grid=self.coggrid, full_grid=True, mean=False)
                self.cog_dist_det[str(self.nstep - 1)] = self.flatten_(
                    1, grid=self.coggrid, full_grid=True, mean=False)

    def step(self):
        self.nstep += 1  # step counter
        if self.nstep == self.intro_time:
            for i in range(self.a2num):
                self.introduce_agents("A2")
        self.schedule.step()
        self.nA1 = np.sum(self.agentgrid == 1)
        self.nA2 = np.sum(self.agentgrid == 2)
        self.collect_hist()
        if self.nstep % 10 == 0:
            sys.stdout.write((str(self.nstep) + " " + str(self.nA1) + " " +
                              str(self.nA2) + "\n"))

    def visualize(self):
        f, ax = plt.subplots(1)
        self.agentgrid = self.agentgrid.astype(int)
        ax.imshow(self.agentgrid,
                  interpolation='nearest',
                  cmap=self.cmap,
                  norm=self.norm)
        # plt.axis("off")
        return (f)
Пример #12
0
class DiseaseSimModel(Model):
    """
    The model class holds the model-level attributes, manages the agents,
    and generally handles
    the global level of our model.

    There is only one model-level parameter: how many agents
    the model contains. When a new model
    is started, we want it to populate itself with the given number of agents.

    The scheduler is a special model component which controls the order
    in which agents are activated.
    """
    def __init__(
            self,
            width=50,
            height=50,
            population_density=0.75,
            vaccine_density=0,
            initial_infection_fraction=0.1,
            initial_vaccination_fraction=0.00,
            prob_infection=0.2,
            prob_agent_movement=0.0,
            disease_planner_config={
                "latent_period_mu": 2 * 4,
                "latent_period_sigma": 0,
                "incubation_period_mu": 5 * 4,
                "incubation_period_sigma": 0,
                "recovery_period_mu": 14 * 4,
                "recovery_period_sigma": 0,
            },
            max_timesteps=200,
            early_stopping_patience=14,
            toric=True,
            seed=None):
        super().__init__()

        self.width = width
        self.height = height
        # fraction of the whole grid that is initiailized with agents
        self.population_density = population_density
        self.vaccine_density = vaccine_density

        self.n_agents = False
        self.n_vaccines = False

        self.initial_infection_fraction = initial_infection_fraction
        self.initial_vaccination_fraction = initial_vaccination_fraction

        self.prob_infection = prob_infection
        self.prob_agent_movement = prob_agent_movement

        self.disease_planner_config = disease_planner_config

        self.max_timesteps = max_timesteps
        self.early_stopping_patience = early_stopping_patience
        self.toric = toric
        self.seed = seed

        self.initialize_observation()
        self.initialize_disease_planner()
        self.initialize_scheduler()
        self.initialize_grid()
        self.initialize_contact_network()
        self.initialize_agents(
            infection_fraction=self.initial_infection_fraction,
            vaccination_fraction=self.initial_vaccination_fraction)
        self.initialize_datacollector()
        self.running = True
        self.datacollector.collect(self)

    ###########################################################################
    ###########################################################################
    # Setup Initialization Helper Functions
    ###########################################################################
    def initialize_observation(self):
        """
        Observation is a nd-array of shape (width, height, num_states)
        where each AgentState will be marked in a separate challenge
        for each of the cells
        """
        self.observation = np.zeros((self.width, self.height, len(AgentState)))

    def initialize_disease_planner(self):
        """
        Initializes a disease planner that the Agents can use to "schedule"
        infection progressions
        """
        self.disease_planner = SEIRDiseasePlanner(
            latent_period_mu=self.disease_planner_config["latent_period_mu"],
            latent_period_sigma=self.
            disease_planner_config["latent_period_sigma"],  # noqa
            incubation_period_mu=self.
            disease_planner_config["incubation_period_mu"],  # noqa
            incubation_period_sigma=self.
            disease_planner_config["incubation_period_sigma"],  # noqa
            recovery_period_mu=self.
            disease_planner_config["recovery_period_mu"],  # noqa
            recovery_period_sigma=self.disease_planner_config[
                "recovery_period_sigma"]  # noqa
        )

    def initialize_scheduler(self):
        """
        Initializes the scheduler
        """
        self.schedule = CustomScheduler(self)

    def initialize_grid(self):
        """
        Initializes the initial Grid
        """
        self.grid = SingleGrid(width=self.width,
                               height=self.height,
                               torus=self.toric)

    def initialize_contact_network(self):
        """
        Initializes the contact network
        """
        self.contact_network = ContactNetwork()

    def initialize_agents(self, infection_fraction, vaccination_fraction):
        """
        Intializes the intial agents on the grid
        """
        assert 0 < self.population_density <= 1, \
            "population_density should be between (0, 1]"

        # Assess the actual population
        self.n_agents = int(self.width * self.height * self.population_density)
        # Assess the available number of vaccines
        self.n_vaccines = int(self.n_agents * self.vaccine_density)

        # Assess the number of agents that
        # have to be infected (the seed infection)
        number_of_agents_to_infect = int(infection_fraction * self.n_agents)
        number_of_agents_to_vaccinate = int(vaccination_fraction *
                                            self.n_agents)

        # Assess the maximum number of vaccines
        # available in the whole simulation
        self.max_vaccines = self.n_vaccines + number_of_agents_to_vaccinate

        for i in range(self.n_agents):
            agent = DiseaseSimAgent(
                unique_id=i,
                model=self,
                prob_agent_movement=self.prob_agent_movement)
            self.schedule.add(agent)
            self.grid.position_agent(agent, x="random", y="random")

            # Update model observation
            # TODO- This has to be refactored to avoid repitition
            agent_x, agent_y = agent.pos
            self.observation[agent_x, agent_y, agent.state.value] = 1

            # Seed the infection in a fraction of the agents
            infection_condition = i < number_of_agents_to_infect
            if infection_condition:
                agent.trigger_infection(prob_infection=1.0)

            # Seed the vaccination in a fraction of the agents
            vaccination_condition = (
                i >= number_of_agents_to_infect and i <
                (number_of_agents_to_infect + number_of_agents_to_vaccinate)
            )  # noqa

            if vaccination_condition:
                agent.set_state(AgentState.VACCINATED)

    def initialize_datacollector(self):
        """
        Setup the initial datacollector
        """
        self.datacollector = DataCollector(
            model_reporters={
                "Susceptible":
                lambda m: m.get_population_fraction_by_state(
                    AgentState.SUSCEPTIBLE),  # noqa
                "Exposed":
                lambda m: m.get_population_fraction_by_state(AgentState.EXPOSED
                                                             ),  # noqa
                "Infectious":
                lambda m: m.get_population_fraction_by_state(
                    AgentState.INFECTIOUS),  # noqa
                "Symptomatic":
                lambda m: m.get_population_fraction_by_state(
                    AgentState.SYMPTOMATIC),  # noqa
                "Recovered":
                lambda m: m.get_population_fraction_by_state(
                    AgentState.RECOVERED),  # noqa
                "Vaccinated":
                lambda m: m.get_population_fraction_by_state(
                    AgentState.VACCINATED),  # noqa
                "R0/10":
                lambda m: m.contact_network.compute_R0() / 10.0
            })

    ###########################################################################
    ###########################################################################
    # State Aggregation
    #       - Functions for easy access/aggregation of simulation wide state
    ###########################################################################

    def get_observation(self):
        # assert self.observation.sum(axis=-1).max() <= 1.0
        # Assertion disabled for perf reasons
        return self.observation

    ###########################################################################
    ###########################################################################
    # Scheduler
    #       - Functions for easy access to scheduler
    ###########################################################################

    def get_scheduler(self):
        return self.schedule

    def get_population_fraction_by_state(self, state: AgentState):
        return self.schedule.get_agent_fraction_by_state(state)

    def is_running(self):
        return self.running

    ###########################################################################
    ###########################################################################
    # Actions
    #        - Functions for actions that can be performed on the model
    ###########################################################################

    def step(self):
        """
        A model step. Used for collecting data and advancing the schedule
        """
        self.propagate_infections()
        self.datacollector.collect(self)
        self.schedule.step()
        self.simulation_completion_checks()

    def vaccinate_cell(self, cell_x, cell_y):
        """
        Vaccinates an agent at cell_x, cell_y, if present

        Response with :
        (is_vaccination_successful, vaccination_response)
        of types
        (boolean, VaccinationResponse)
        """

        # Case 0 : No vaccines left
        if self.n_vaccines <= 0:
            return False, VaccinationResponse.AGENT_VACCINES_EXHAUSTED
        self.n_vaccines -= 1

        # Case 1 : Cell is empty
        if self.grid.is_cell_empty((cell_x, cell_y)):
            return False, VaccinationResponse.CELL_EMPTY

        agent = self.grid[cell_x][cell_y]
        if agent.state == AgentState.SUSCEPTIBLE:
            # Case 2 : Agent is susceptible, and can be vaccinated
            agent.set_state(AgentState.VACCINATED)
            return True, VaccinationResponse.VACCINATION_SUCCESS
        elif agent.state == AgentState.EXPOSED:
            # Case 3 : Agent is already exposed, and its a waste of vaccination
            return False, VaccinationResponse.AGENT_EXPOSED
        elif agent.state == AgentState.INFECTIOUS:
            # Case 4 : Agent is already infectious,
            # and its a waste of vaccination
            return False, VaccinationResponse.AGENT_INFECTIOUS
        elif agent.state == AgentState.SYMPTOMATIC:
            # Case 5 : Agent is already Symptomatic,
            # and its a waste of vaccination
            return False, VaccinationResponse.AGENT_SYMPTOMATIC
        elif agent.state == AgentState.RECOVERED:
            # Case 6 : Agent is already Recovered,
            # and its a waste of vaccination
            return False, VaccinationResponse.AGENT_RECOVERED
        elif agent.state == AgentState.VACCINATED:
            # Case 7 : Agent is already Vaccination,
            # and its a waste of vaccination
            return False, VaccinationResponse.AGENT_VACCINATED
        raise NotImplementedError()

    ###########################################################################
    ###########################################################################
    # Misc
    ###########################################################################

    def simulation_completion_checks(self):
        """
        Simulation is complete if :
            - if the timesteps have exceeded the number of max_timesteps
            or
            - the fraction of susceptible population is <= 0
            or
            - the fraction of susceptible population has not changed since the
            last N timesteps
        """
        if self.schedule.steps > self.max_timesteps - 1:
            self.running = False
            return

        susceptible_population = self.get_population_fraction_by_state(
            AgentState.SUSCEPTIBLE)
        if susceptible_population <= 0:
            self.running = False
            return

        if self.schedule.steps > self.early_stopping_patience:
            last_N_susceptible_population = \
                self.datacollector.model_vars["Susceptible"][-1 *
                                                             self.early_stopping_patience:]  # noqa
            if len(set(last_N_susceptible_population)) == 1:
                self.running = False
                return

    def tick(self):
        """
        a mirror function for the internal step function
        to help avoid confusion in the RL codebases (with the RL step)
        """
        self.step()

    def propagate_infections(self):
        """
        Propagates infection during a single simulation step
        """
        valid_infectious_agents = []
        valid_infectious_agents += self.schedule.get_agents_by_state(
            AgentState.INFECTIOUS)
        valid_infectious_agents += self.schedule.get_agents_by_state(
            AgentState.SYMPTOMATIC)

        for _infectious_agent in valid_infectious_agents:
            target_candidates = self.grid.get_neighbors(
                pos=_infectious_agent.pos,
                moore=True,
                include_center=False,
                radius=1)
            for _target_candidate in target_candidates:
                if _target_candidate.state == AgentState.SUSCEPTIBLE:
                    was_infection_successful =\
                         _target_candidate.trigger_infection(
                            prob_infection=self.prob_infection)
                    if was_infection_successful:
                        # Register infection in the contact network
                        self.contact_network.register_infection_spread(
                            _infectious_agent, _target_candidate)
Пример #13
0
class DiseaseModel(Model):
	"""
	A model with some number of agents.
	highS: Number of agents with high sociability.
	middleS: Number of agents with middle sociability.
	lowS: Number of agents with low sociability.
	width: Width of the grid.
	height: Height of the grid.
	edu_setting: Classrooms and set schedule if true, else random free movement.
	cureProb: Probability of agent getting better.
	cureProbFac: Factor of cureProb getting higher.
	mutateProb: Probability of a disease mutating.
	diseaseRate: Rate at which the disease spreads.
	"""
	def __init__(self, highS, middleS, lowS, width, height, edu_setting=True, cureProb=0.1, cureProbFac=2/1440, mutateProb=0.0050, diseaseRate=0.38):
		super().__init__()
		self.num_agents = highS + middleS + lowS
		self.lowS = lowS
		self.middleS = middleS
		self.highS = highS
		self.initialCureProb = cureProb
		self.cureProbFac = cureProbFac
		self.mutateProb = mutateProb
		self.diseaseRate = diseaseRate
		self.edu_setting = edu_setting
		self.maxDisease = 0# amount of mutations
		self.counter = 540 # keeps track of timesteps
		self.removed = []
		self.exit = (width-1,floor(height/2))
		# Check if agents fit within grid
		if self.num_agents > width * height:
			raise ValueError("Number of agents exceeds grid capacity.")

		# Create grid with random activation
		self.grid = SingleGrid(width, height, True)
		self.schedule = RandomActivation(self)

		if edu_setting:
			# Create walls
			numberRooms = 3
			self.add_walls(numberRooms, width, height)
			self.midWidthRoom = floor(width / numberRooms / 2)
			self.midHeightRoom = floor(height / numberRooms / 2)

			# Calculate the centers of the 6 rooms
			roomLeftDown = (5 * self.midWidthRoom, self.midHeightRoom)
			roomLeftMid = (3 * self.midWidthRoom, self.midHeightRoom)
			roomLeftUp = (self.midWidthRoom, self.midHeightRoom)
			roomRightDown = (5 * self.midWidthRoom, 5 * self.midHeightRoom, )
			roomRightMid = (3 * self.midWidthRoom, 5 * self.midHeightRoom)
			roomRightUp = (self.midWidthRoom, 5 * self.midHeightRoom)

			# Set 3 goals per roster
			self.roster = [[roomLeftDown, roomLeftUp, roomRightMid], [roomRightMid, roomLeftDown, roomRightDown],
							[roomRightUp, roomRightDown, roomLeftUp]]

		# Create agents
		self.addAgents(lowS, 0, 0)
		self.addAgents(middleS, lowS, 1)
		self.addAgents(highS, lowS + highS, 2)

		self.datacollector = DataCollector(
			model_reporters={"diseasepercentage": disease_collector},
			agent_reporters={"disease": "disease"})

	def heuristic(self, start, goal):
		"""
		Returns manhattan distance.
		start: current location (x,y)
		goal: goal location (x,y)
		"""
		dx = abs(start[0] - goal[0])
		dy = abs(start[1] - goal[1])
		return dx + dy

	def get_vertex_neighbors(self, pos):
		"""
		Returns all neighbors.
		pos: current position
		"""
		n = self.grid.get_neighborhood(pos, moore=False)
		neighbors = []
		for item in n:
			if not abs(item[0]-pos[0]) > 1 and not abs(item[1]-pos[1]) > 1:
				neighbors += [item]
		return neighbors

	def move_cost(self, location):
		"""
		Return the cost of a location.
		"""
		if self.grid.is_cell_empty(location):
			return 1 # Normal movement cost
		else:
			return 100

	def add_walls(self, n, widthGrid, heightGrid):
		"""
		Add walls in grid.
		n: number of rooms horizontally
		widthGrid: width of the grid
		heightGrid: height of the grid
		"""
		widthRooms = floor(widthGrid/n)
		heightRooms = floor(heightGrid/n)
		widthHall = widthGrid - 2 * widthRooms
		heightHall = heightGrid - 2 * heightRooms
		# Add horizontal walls
		for i in range(n - 1):
			for y in range(heightRooms):
				brick = wall(self.num_agents, self)
				self.grid.place_agent(brick, ((i + 1) * widthRooms, y))
				self.grid.place_agent(brick, ((i + 1) * widthRooms, y + heightRooms + heightHall))
		doorWidth = 2
		# Add vertical walls
		for x in range(widthGrid):
			if (x % widthRooms) < (widthRooms - doorWidth):
				brick = wall(self.num_agents, self)
				self.grid.place_agent(brick, (x, heightRooms))
				self.grid.place_agent(brick, (x, heightRooms + heightHall - 1))

	def addAgents(self, n, startID, sociability):
		"""
		Add agents with a sociability.
		n: number of agents
		startID: ID of the first added agent
		sociability: sociability of the agents
		"""
		disease_list = np.random.randint(0,2,n)
		for i in range(n):
			a = DiseaseAgent(i + startID, sociability,self,disease_list[i])
			self.schedule.add(a)
			# Add the agent to a random grid cell
			location = self.grid.find_empty()
			self.grid.place_agent(a, location)
	def step(self):
		"""
		Continue one step in simulation.
		"""
		self.counter += 1
		self.datacollector.collect(self)
		self.schedule.step()
Пример #14
0
class SchoolModel(Model):
    """
    Model class for the Schelling segregation model.

    ...

    Attributes
    ----------

    height: int
        grid height
    width: int
        grid width
    num_schools:  int
        number of schools
    f : float
        fraction preference of agents for like
    M : float
        utility penalty for homogeneous neighbourhood
    residential_steps :
        number of steps for the residential model
    minority_pc :
        minority fraction
    bounded : boolean
        If True use bounded (predefined neighbourhood) for agents residential choice
    cap_max : float
        school capacity TODO: explain
    radius : int
        neighbourhood radius for agents calculation of residential choice (only used if not bounded)
    household_types :
        labels for different ethnic types of households
    symmetric_positions :
        use symmetric positions for the schools along the grid, or random
    schelling :
        if True use schelling utility function otherwise use assymetric
    school_pos :
        if supplied place schools in the supplied positions - also update school_num
    extended_data :
        if True collect extra data for agents (utility distribution and satisfaction)
        takes up a lot of space
    sample : int
        subsample the empty residential sites to be evaluated to speed up computation
    variable_f : variable_f
        draw values of the ethnic preference, f from a normal distribution
    sigma : float
        The standard deviation of the normal distribution used for f
    alpha : float
        ratio of ethnic to distance to school preference for school utility
    temp : float
        temperature for the behavioural logit rule for agents moving
    households : list
        all household objects
    schools : list
        all school objects
    residential_moves_per_step : int
        number of agents to move residence at every step
    school_moves_per_step : int
        number of agents to move school at every step
    num_households : int
        total number of household agents
    pm : list [ , ]
        number of majority households, number of minority households
    schedule : mesa schedule type
    grid : mesa grid type
    total_moves :
        number of school moves made in particular step
    res_moves :
        number of residential site moves made in particular step
    move :
        type of move recipe - 'random' 'boltzmann' or 'deterministic'
    school_locations : list
       list of locations of all schools (x,y)
    household_locations :
       list of locations of all households (x,y)
    closer_school_from_position : numpy array shape : (width x height)
        map of every grid position to the closest school

    """


    def __init__(self, height=100, width=100, density=0.9, num_neighbourhoods=16, schools_per_neighbourhood=2,minority_pc=0.5, homophily=3, f0=0.6,f1=0.6,\
                 M0=0.8,M1=0.8,T=0.75,
                 alpha=0.5, temp=1, cap_max=1.01, move="boltzmann", symmetric_positions=True,
                 residential_steps=70,schelling=False,bounded=True,
                 residential_moves_per_step=2000, school_moves_per_step =2000,radius=6,proportional = False,
                 torus=False,fs="eq", extended_data = False, school_pos=None, agents=None, sample=4, variable_f=True, sigma=0.35, displacement=8 ):

        # Options  for the model
        self.height = height
        self.width = width
        print("h x w", height, width)
        self.density = density
        #self.num_schools= num_schools
        self.f = [f0, f1]
        self.M = [M0, M1]
        self.residential_steps = residential_steps
        self.minority_pc = minority_pc
        self.bounded = bounded
        self.cap_max = cap_max
        self.T = T
        self.radius = radius
        self.household_types = [0, 1]  # majority, minority !!
        self.symmetric_positions = symmetric_positions
        self.schelling = schelling
        self.school_pos = school_pos
        self.extended_data = extended_data
        self.sample = sample
        self.variable_f = variable_f
        self.sigma = sigma
        self.fs = fs

        # choice parameters
        self.alpha = alpha
        self.temp = temp

        self.households = []
        self.schools = []
        self.neighbourhoods = []
        self.residential_moves_per_step = residential_moves_per_step
        self.school_moves_per_step = school_moves_per_step

        self.num_households = int(width * height * density)
        num_min_households = int(self.minority_pc * self.num_households)
        self.num_neighbourhoods = num_neighbourhoods
        self.schools_per_neigh = schools_per_neighbourhood
        self.num_schools = int(num_neighbourhoods * self.schools_per_neigh)
        self.pm = [
            self.num_households - num_min_households, num_min_households
        ]

        self.schedule = RandomActivation(self)
        self.grid = SingleGrid(height, width, torus=torus)
        self.total_moves = 0
        self.res_moves = 0

        self.move = move

        self.school_locations = []
        self.household_locations = []
        self.neighbourhood_locations = []
        self.closer_school_from_position = np.empty(
            [self.grid.width, self.grid.height])
        self.closer_neighbourhood_from_position = np.empty(
            [self.grid.width, self.grid.height])

        self.happy = 0
        self.res_happy = 0
        self.percent_happy = 0
        self.seg_index = 0
        self.res_seg_index = 0
        self.residential_segregation = 0
        self.collective_utility = 0
        self.comp0,self.comp1,self.comp2,self.comp3,self.comp4,self.comp5,self.comp6,self.comp7, \
        self.comp8, self.comp9, self.comp10, self.comp11, self.comp12, self.comp13, self.comp14, self.comp15 = 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        self.satisfaction = []
        self.pi_jm = []
        self.pi_jm_fixed = []
        self.compositions = []
        self.average_like_fixed = 0
        self.average_like_variable = 0

        self.my_collector = []
        if torus:
            self.max_dist = self.height / np.sqrt(2)
        else:
            self.max_dist = self.height * np.sqrt(2)

        # Set up agents
        # We use a grid iterator that returns
        # the coordinates of a cell as well as
        # its contents. (coord_iter)
        # Set up schools in symmetric positions along the grid

        # if schools already supplied place them where they should be
        # TODO: fix
        if self.school_pos:
            school_positions = self.school_pos
            self.school_locations = school_pos
            self.num_schools = len(school_pos)
            print("Option not working")
            sys.exit()

        # otherwise calculate the positions
        else:
            if self.num_neighbourhoods == 4:
                neighbourhood_positions = [(width / 4, height / 4),
                                           (width * 3 / 4, height / 4),
                                           (width / 4, height * 3 / 4),
                                           (width * 3 / 4, height * 3 / 4)]
            elif self.num_neighbourhoods == 9:
                n = 6
                neighbourhood_positions = [(width/n,height/n),(width*3/n,height*1/n),(width*5/n,height*1/n),(width/n,height*3/n),\
                                    (width*3/n,height*3/n),(width*5/n,height*3/n),(width*1/n,height*5/n),(width*3/n,height*5/n),\
                                    (width*5/n,height*5/n)]

            elif self.num_neighbourhoods in [25, 64, 16]:
                neighbourhood_positions = []
                n = int(np.sqrt(self.num_neighbourhoods) * 2)
                print(n)
                x1 = range(1, int(n + 1), 2)

                xloc = np.repeat(x1, int(n / 2))
                yloc = np.tile(x1, int(n / 2))

                for i in range(self.num_neighbourhoods):
                    neighbourhood_positions.append(
                        (xloc[i] * height / n, yloc[i] * width / n))

        print(neighbourhood_positions)
        #for i in range(self.num_schools):i
        i = 0
        while len(self.neighbourhoods) < self.num_neighbourhoods:

            if self.symmetric_positions or self.school_pos:
                x = int(neighbourhood_positions[i][0])
                y = int(neighbourhood_positions[i][1])

                #print(x,y)

            else:
                x = random.randrange(start=2, stop=self.grid.width - 2)
                y = random.randrange(start=2, stop=self.grid.height - 2)

            pos = (x, y)
            pos2 = (x + 1, y + 1)
            if schools_per_neighbourhood == 2:
                pos3 = (x - displacement, y - displacement)
                pos2 = (x + displacement, y + displacement)

            do_not_use = self.school_locations + self.neighbourhood_locations
            #if (pos not in do_not_use) and (pos2 not in do_not_use ) and (pos3 not in do_not_use ):
            if (pos not in do_not_use) and (pos2 not in do_not_use):

                #print('pos',pos,pos2,pos3)
                self.school_locations.append(pos2)
                school = SchoolAgent(pos2, self)
                self.grid.place_agent(school, school.unique_id)
                self.schools.append(school)
                self.schedule.add(school)

                if self.schools_per_neigh == 2:
                    # Add another school
                    self.school_locations.append(pos3)
                    school = SchoolAgent(pos3, self)
                    self.grid.place_agent(school, school.unique_id)
                    self.schools.append(school)
                    self.schedule.add(school)

                self.neighbourhood_locations.append(pos)
                neighbourhood = NeighbourhoodAgent(pos, self)
                self.grid.place_agent(neighbourhood, neighbourhood.unique_id)
                self.neighbourhoods.append(neighbourhood)
                self.schedule.add(neighbourhood)

            else:
                print(pos, pos2, pos3, "is found in", do_not_use)
            i += 1
        print("num_schools", len(self.school_locations))

        print("schools completed")

        #print(self.neighbourhood_locations)
        #print("schools",self.school_locations, len(self.school_locations))
        # Set up households

        # If agents are supplied place them where they need to be
        if agents:

            for cell in agents:
                [agent_type, x, y] = cell
                if agent_type in [0, 1]:

                    pos = (x, y)
                    if self.grid.is_cell_empty(pos):
                        agent = HouseholdAgent(pos, self, agent_type)
                        self.grid.place_agent(agent, agent.unique_id)

                        self.household_locations.append(pos)
                        self.households.append(agent)
                        self.schedule.add(agent)

        # otherwise produce them
        else:

            # create household locations but dont create agents yet

            while len(self.household_locations) < self.num_households:

                #Add the agent to a random grid cell
                x = random.randrange(self.grid.width)
                y = random.randrange(self.grid.height)
                pos = (x, y)

                if (pos not in (self.school_locations +
                                self.household_locations +
                                self.neighbourhood_locations)):
                    self.household_locations.append(pos)

            #print(Dij)

            for ind, pos in enumerate(self.household_locations):

                # create a school or create a household

                if ind < int(self.minority_pc * self.num_households):
                    agent_type = self.household_types[1]
                else:
                    agent_type = self.household_types[0]

                household_index = ind
                agent = HouseholdAgent(pos, self, agent_type, household_index)
                #decorator_agent = HouseholdAgent(pos, self, agent_type)

                self.grid.place_agent(agent, agent.unique_id)

                #self.grid.place_agent(decorator_agent, pos)

                self.households.append(agent)
                self.schedule.add(agent)

        self.set_positions_to_school()
        self.set_positions_to_neighbourhood()
        self.calculate_all_distances()
        self.calculate_all_distances_to_neighbourhoods()

        for agent in self.households:

            random_school_index = random.randint(0, len(self.schools) - 1)
            #print("school_index", random_school_index, agent.Dj, len(agent.Dj))

            candidate_school = self.schools[random_school_index]
            agent.allocate(candidate_school, agent.Dj[random_school_index])

            #closer_school = self.schools[p.argmin(Dj)]
            #closer_school.students.append(agent)
        # agent.allocate(closer_school, np.min(Dj))
        #print(agent.school.unique_id)

        self.pi_jm = np.zeros(shape=(len(self.school_locations),
                                     len(self.household_types)))
        self.local_compositions = np.zeros(shape=(len(self.school_locations),
                                                  len(self.household_types)))
        self.avg_school_size = round(density * width * height /
                                     (len(self.schools)))

        if self.extended_data:
            self.datacollector = DataCollector(
                model_reporters={
                    "agent_count": lambda m: m.schedule.get_agent_count(),
                    "seg_index": "seg_index",
                    "residential_segregation": "residential_segregation",
                    "res_seg_index": "res_seg_index",
                    "fixed_res_seg_index": "fixed_res_seg_index",
                    "happy": "happy",
                    "percent_happy": "percent_happy",
                    "total_moves": "total_moves",
                    "compositions0": "compositions0",
                    "compositions1": "compositions1",
                    "comp0": "comp0",
                    "comp1": "comp1",
                    "comp2": "comp2",
                    "comp3": "comp3",
                    "comp4": "comp4",
                    "comp5": "comp5",
                    "comp6": "comp6",
                    "comp7": "comp7",
                    "compositions": "compositions",
                    "collective_utility": "collective_utility"
                },
                agent_reporters={
                    "local_composition": "local_composition",
                    "type": lambda a: a.type,
                    "id": lambda a: a.unique_id,
                    #"fixed_local_composition": "fixed_local_composition",
                    #"variable_local_composition": "variable_local_composition",
                    "school_utilities": "school_utilities",
                    "residential_utilities": "residential_utilities",
                    "pos": "pos"
                })

        else:
            self.datacollector = DataCollector(
                model_reporters={
                    "agent_count": lambda m: m.schedule.get_agent_count(),
                    "seg_index": "seg_index",
                    "residential_segregation": "residential_segregation",
                    "res_seg_index": "res_seg_index",
                    "fixed_res_seg_index": "fixed_res_seg_index",
                    "happy": "happy",
                    "percent_happy": "percent_happy",
                    "total_moves": "total_moves",
                    "compositions0": "compositions0",
                    "compositions1": "compositions1",
                    "comp0": "comp0",
                    "comp1": "comp1",
                    "comp2": "comp2",
                    "comp3": "comp3",
                    "comp4": "comp4",
                    "comp5": "comp5",
                    "comp6": "comp6",
                    "comp7": "comp7",
                    "compositions": "compositions",
                    "collective_utility": "collective_utility"
                },
                agent_reporters={
                    "local_composition": "local_composition",
                    "type": lambda a: a.type,
                    "id": lambda a: a.unique_id,
                    # "fixed_local_composition": "fixed_local_composition",
                    # "variable_local_composition": "variable_local_composition",
                    "pos": "pos"
                })

        # Calculate local composition
        # set size
        for school in self.schools:
            #school.get_local_school_composition()
            #cap = round(np.random.normal(loc=cap_max * self.avg_school_size, scale=self.avg_school_size * 0.05))
            cap = self.avg_school_size * self.cap_max
            school.capacity = cap
            print("cap", self.avg_school_size, cap)
            segregation_index(self)
        #

        print(
            "height = %d; width = %d; density = %.2f; num_schools = %d; minority_pc =  %.2f; "
            "f0 =  %.2f; f1 =  %.2f; M0 =  %.2f; M1 =  %.2f;\
        alpha =  %.2f; temp =  %.2f; cap_max =  %.2f; move = %s; symmetric_positions = %s"
            % (height, width, density, self.num_schools, minority_pc, f0, f1,
               M0, M1, alpha, temp, cap_max, move, symmetric_positions))

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

    def calculate_all_distances(self):
        """

        calculate distance between school and household
        Euclidean or gis shortest road route
        :return: dist

        """

        Dij = distance.cdist(np.array(self.household_locations),
                             np.array(self.school_locations), 'euclidean')

        for household_index, household in enumerate(self.households):
            Dj = Dij[household_index, :]
            household.Dj = Dj

            # Calculate distances of the schools - define the school-neighbourhood and compare
            # closer_school = household.schools[np.argmin(household.)]
            closer_school_index = np.argmin(household.Dj)
            household.closer_school = self.schools[closer_school_index]
            household.closer_school.neighbourhood_students.append(household)

        return (Dij)

    def calculate_all_distances_to_neighbourhoods(self):
        """

        calculate distance between school and household
        Euclidean or gis shortest road route
        :return: dist

        """
        for household_index, household in enumerate(self.households):

            # Calculate distances of the schools - define the school-neighbourhood and compare
            # closer_school = household.schools[np.argmin(household.)]
            household.closer_neighbourhood = self.get_closer_neighbourhood_from_position(
                household.pos)
            household.closer_neighbourhood.neighbourhood_students_indexes.append(
                household_index)

        # just sanity check
        # for i, neighbourhood in enumerate(self.neighbourhoods):
        #     students = neighbourhood.neighbourhood_students_indexes
        #     print("students,",i, len(students))

    def set_positions_to_school(self):
        '''
        calculate closer school from every position on the grid
        Euclidean or gis shortest road route
        :return: dist
        '''
        distance_dict = {}
        # Add the agent to a random grid cell

        all_grid_locations = []

        for x in range(self.grid.width):
            for y in range(self.grid.height):
                all_grid_locations.append((x, y))

        Dij = distance.cdist(np.array(all_grid_locations),
                             np.array(self.school_locations), 'euclidean')

        for i, pos in enumerate(all_grid_locations):
            Dj = Dij[i, :]
            (x, y) = pos
            # Calculate distances of the schools - define the school-neighbourhood and compare
            # closer_school = household.schools[np.argmin(household.)]
            closer_school_index = np.argmin(Dj)
            self.closer_school_from_position[x][y] = closer_school_index

        #print("closer_school_by_position",self.closer_school_from_position)

    def set_positions_to_neighbourhood(self):
        '''
        calculate closer neighbourhood centre from every position on the grid
        Euclidean or gis shortest road route
        :return: dist
        '''
        distance_dict = {}
        # Add the agent to a random grid cell

        all_grid_locations = []

        for x in range(self.grid.width):
            for y in range(self.grid.height):
                all_grid_locations.append((x, y))

        Dij = distance.cdist(np.array(all_grid_locations),
                             np.array(self.neighbourhood_locations),
                             'euclidean')

        for i, pos in enumerate(all_grid_locations):
            Dj = Dij[i, :]
            (x, y) = pos
            # Calculate distances of the schools - define the school-neighbourhood and compare
            # closer_school = household.schools[np.argmin(household.)]
            closer_neighbourhood_index = np.argmin(Dj)
            self.closer_neighbourhood_from_position[x][
                y] = closer_neighbourhood_index

        #print("closer_school_by_position", self.closer_school_from_position)

    def get_closer_school_from_position(self, pos):
        """
        :param pos: (x,y) position
        :return school: school object closest to this position
        """
        (x, y) = pos
        school_index = self.closer_school_from_position[x][y]
        school = self.get_school_from_index(school_index)

        return (school)

    def get_closer_neighbourhood_from_position(self, pos):
        """
        :param pos: (x,y) position
        :return school: school object closest to this position
        """
        (x, y) = pos
        neighbourhood_index = self.closer_neighbourhood_from_position[x][y]
        neighbourhood = self.get_neighbourhood_from_index(neighbourhood_index)

        return (neighbourhood)

    def get_school_from_index(self, school_index):
        """
        :param self: obtain the school object using the index
        :param school_index:
        :return: school object
        """

        return (self.schools[int(school_index)])

    def get_neighbourhood_from_index(self, neighbourhood_index):
        """
        :param self: obtain the school object using the index
        :param school_index:
        :return: school object
        """

        return (self.neighbourhoods[int(neighbourhood_index)])

    def get_households_from_index(self, household_indexes):
        """
        Retrieve household objects from their indexes
        :param household_indexes: list of indexes to retrieve household objects
        :return: households: household objects
        """
        households = []
        for household_index in household_indexes:
            households.append(self.households[household_index])
        return (households)

    def step(self):
        '''
        Run one step of the model. If All agents are happy, halt the model.
        '''
        self.happy = 0  # Reset counter of happy agents
        self.res_happy = 0
        self.total_moves = 0
        self.total_considered = 0
        self.res_moves = 0
        self.satisfaction = []
        self.res_satisfaction = []

        self.schedule.step()

        satisfaction = 0
        res_satisfaction = 0
        print("happy", self.happy)
        print("total_considered", self.total_considered)

        # Once residential steps are done calculate school distances

        if self.schedule.steps <= self.residential_steps or self.schedule.steps == 1:
            # during the residential steps keep recalculating the school neighbourhood compositions
            # this is required for the neighbourhoods metric

            #print("recalculating neighbourhoods")
            # TODO: check this, not sure if this and the recalculation below is needed
            for school in self.schools:
                school.neighbourhood_students = []
            for neighbourhood in self.neighbourhoods:
                neighbourhood.neighbourhood_students_indexes = []

            # update the household locations after a move
            self.household_locations = []
            for i, household in enumerate(self.households):
                self.household_locations.append(household.pos)

            self.calculate_all_distances()
            self.calculate_all_distances_to_neighbourhoods()
            #print("all", self.calculate_all_distances()[i, :])

            # for i, household in enumerate(self.households):
            #     print(household.calculate_distances())
            #     # Calculate distances of the schools - define the school-neighbourhood and compare
            #     # closer_school = household.schools[np.argmin(household.)]
            #     closer_school_index = np.argmin(household.Dj)
            #     household.closer_school = self.schools[closer_school_index]
            #     household.closer_school.neighbourhood_students.append(household)
            #
            #     # Initialize house allocation to school
            #     #household.move_school(closer_school_index, self.schools[closer_school_index])
            #

            self.residential_segregation = segregation_index(
                self, unit="neighbourhood")
            self.res_seg_index = segregation_index(self,
                                                   unit="agents_neighbourhood")
            self.fixed_res_seg_index = segregation_index(
                self, unit="fixed_agents_neighbourhood", radius=1)
            res_satisfaction = np.mean(self.res_satisfaction)

        satisfaction = 0
        # calculate these after residential_model
        if self.schedule.steps > self.residential_steps:
            self.collective_utility = calculate_collective_utility(self)
            print(self.collective_utility)
            self.seg_index = segregation_index(self)
            satisfaction = np.mean(self.satisfaction)



        print("seg_index", "%.2f"%(self.seg_index), "var_res_seg", "%.2f"%(self.res_seg_index), "neighbourhood",
              "%.2f"%(self.residential_segregation), "fixed_res_seg_index","%.2f"%(self.fixed_res_seg_index), \
              "res_satisfaction %.2f" %res_satisfaction,"satisfaction %.2f" %satisfaction,\
              "average_like_fixed %.2f"%self.average_like_fixed,"average_like_var %.2f"%self.average_like_variable  )

        if self.happy == self.schedule.get_agent_count():
            self.running = False

        compositions = []

        # remove this?
        for school in self.schools:
            self.my_collector.append([
                self.schedule.steps, school.unique_id,
                school.get_local_school_composition()
            ])
            self.compositions = school.get_local_school_composition()
            compositions.append(school.get_local_school_composition()[0])
            compositions.append(school.get_local_school_composition()[1])

            self.compositions1 = int(school.get_local_school_composition()[1])
            self.compositions0 = int(school.get_local_school_composition()[0])
            #print("school_students",school.neighbourhood_students)

        #print("comps",compositions,np.sum(compositions) )
        [
            self.comp0, self.comp1, self.comp2, self.comp3, self.comp4,
            self.comp5, self.comp6, self.comp7
        ] = compositions[0:8]
        # collect data
        #
        self.datacollector.collect(self)
        print("moves", self.total_moves, "res_moves", self.res_moves,
              "percent_happy", self.percent_happy)

        for i, household in enumerate(self.households):
            household.school_utilities = []
            household.residential_utilities = []
Пример #15
0
class SeparationBarrierModel(Model):
    def __init__(self, height, width, palestinian_density, settlement_density,
                 settlers_violence_rate, settlers_growth_rate, suicide_rate, greed_level,
                 settler_vision=1, palestinian_vision=1, 
                 movement=True, max_iters=1000):

        super(SeparationBarrierModel, self).__init__()
        self.height = height
        self.width = width
        self.palestinian_density = palestinian_density
        self.settler_vision = settler_vision
        self.palestinian_vision = palestinian_vision
        self.settlement_density = settlement_density
        self.movement = movement
        self.running = True
        self.max_iters = max_iters
        self.iteration = 0
        self.schedule = RandomActivation(self)
        self.settlers_violence_rate = settlers_violence_rate
        self.settlers_growth_rate = settlers_growth_rate
        self.suicide_rate = suicide_rate
        self.greed_level = greed_level

        self.total_violence = 0

        self.grid = SingleGrid(height, width, torus=False)

        model_reporters = {
        }
        agent_reporters = {
#           "x": lambda a: a.pos[0],
#           "y": lambda a: a.pos[1],
        }
        self.dc = DataCollector(model_reporters=model_reporters,
                                agent_reporters=agent_reporters)
        self.unique_id = 0

        # Israelis and palestinans split the region in half
        for (contents, x, y) in self.grid.coord_iter():
            if random.random() < self.palestinian_density:
                palestinian = Palestinian(self.unique_id, (x, y), vision=self.palestinian_vision, breed="Palestinian",
                          model=self)
                self.unique_id += 1
                self.grid.position_agent(palestinian, x,y)
                self.schedule.add(palestinian)
            elif ((y > (self.grid.height) * (1-self.settlement_density)) and random.random() < self.settlement_density):
                settler = Settler(self.unique_id, (x, y),
                                  vision=self.settler_vision, model=self, breed="Settler")
                self.unique_id += 1
                self.grid.position_agent(settler, x,y)
                self.schedule.add(settler)

    def add_settler(self, pos):
        settler = Settler(self.unique_id, pos,
                          vision=self.settler_vision, model=self, breed="Settler")
        self.unique_id += 1
        self.grid.position_agent(settler, pos[0], pos[1])
        self.schedule.add(settler)

    def set_barrier(self,victim_pos, violent_pos):
        #print("Set barrier - Greed level", self.greed_level)
        visible_spots = self.grid.get_neighborhood(victim_pos,
                                                        moore=True, radius=self.greed_level + 1)
        furthest_empty  = self.find_furthest_empty_or_palestinian(victim_pos, visible_spots)
        x,y = furthest_empty
        current = self.grid[y][x]
        #print ("Set barrier!!", pos, current)
        free = True
        if (current is not None and current.breed == "Palestinian"):
            #print ("Relocating Palestinian")
           free =  self.relocate_palestinian(current, current.pos)

        if (free):
            barrier = Barrier(-1, furthest_empty, model=self)
            self.grid.position_agent(barrier, x,y)
        
        # Relocate the violent palestinian
        #violent_x, violent_y = violent_pos
        #if violent_pos != furthest_empty:
        #    violent_palestinian = self.grid[violent_y][violent_x]
        #    self.relocate_palestinian(violent_palestinian, furthest_empty)

    def relocate_palestinian(self, palestinian, destination):
        #print ("Relocating Palestinian in ", palestinian.pos, "To somehwhere near ", destination)
        visible_spots = self.grid.get_neighborhood(destination,
                                                        moore=True, radius=palestinian.vision)
        nearest_empty = self.find_nearest_empty(destination, visible_spots)
        #print("First Nearest empty to ", palestinian.pos, " Is ", nearest_empty)
        if (nearest_empty):
            self.grid.move_agent(palestinian, nearest_empty)
        else:
            #print ("Moveing to random empty")
            if (self.grid.exists_empty_cells()):
                self.grid.move_to_empty(palestinian)
            else:
                return False

        return True

    def find_nearest_empty(self, pos, neighborhood):
        nearest_empty = None
        sorted_spots = self.sort_neighborhood_by_distance(pos, neighborhood)
        index = 0
        while (nearest_empty is None and index < len(sorted_spots)):
            if self.grid.is_cell_empty(sorted_spots[index]):
                nearest_empty = sorted_spots[index]
            index += 1

        return nearest_empty

    def find_furthest_empty_or_palestinian(self, pos, neighborhood):
        furthest_empty = None
        sorted_spots = self.sort_neighborhood_by_distance(pos, neighborhood)
        sorted_spots.reverse()
        index = 0
        while (furthest_empty is None and index < len(sorted_spots)):
            spot = sorted_spots[index]
            if self.grid.is_cell_empty(spot) or self.grid[spot[1]][spot[0]].breed == "Palestinian" :
                furthest_empty = sorted_spots[index]
            index += 1

        return furthest_empty



    def sort_neighborhood_by_distance(self, from_pos, neighbor_spots):
        from_x, from_y = from_pos
        return sorted(neighbor_spots, key = lambda spot: self.eucledean_distance(from_x, spot[0], from_y, spot[1], self.grid.width, self.grid.height))


    def eucledean_distance(self, x1,x2,y1,y2,w,h):
        # http://stackoverflow.com/questions/2123947/calculate-distance-between-two-x-y-coordinates
        return math.sqrt(min(abs(x1 - x2), w - abs(x1 - x2)) ** 2 + min(abs(y1 - y2), h - abs(y1-y2)) ** 2)
        

    def step(self):
        """
        Advance the model by one step and collect data.
        """
        self.violence_count = 0
      #  for i in range(100):
        self.schedule.step()
        self.total_violence += self.violence_count
      #  average = self.violence_count / 100
        #print("Violence average %f " % average)
        print("Total Violence: ", self.total_violence)
class SupermarketModel(Model):
    def __init__(self, type=QueueType.CLASSIC, seed=None):
        np.random.seed(seed)

        # Mesa internals
        self.running = True
        self.steps_in_day = 7200

        # World related
        self.queue_type = QueueType[type]
        self.terrain_map_name = 'map' if self.queue_type == QueueType.CLASSIC else 'map_snake'
        with open(os.path.join(os.getcwd(), '..', 'resources', '{}.txt'.format(self.terrain_map_name))) as f:
            self.width, self.height = map(int, f.readline().strip().split(' '))
            self.capacity = int(f.readline().strip())
            self.world = [list(c) for c in f.read().split('\n') if c]

        self.grid = SingleGrid(self.width, self.height, True)

        # Agent related
        self.generated_customers_count = 0
        self.schedule = BaseScheduler(self)

        self.entry_points = []
        self.queues = {}
        self.queue_length_limit = 5
        self.cashiers = {}
        # TODO: Merge position (cash_registers) and open
        # attribute (open_cashier) with cashiers dict
        self.cash_registers = {}
        self.open_cashier = set()

        # Pathfinding
        self.finder = IDAStarFinder()
        self.snake_entry = None
        self.snake_exit = None

        # Populate grid from world
        for col, line in enumerate(self.world):
            for row, cell in enumerate(line):
                if cell == 'X':
                    self.grid[row][col] = ObstacleAgent('{}:{}'.format(col, row), self)
                elif cell == 'S':
                    self.snake_entry = (row, col)
                elif cell == 'Z':
                    self.snake_exit = (row, col)
                elif cell in ['1', '2', '3', '4', '5']:
                    cash_register = CashRegisterAgent(cell, self, (row, col))
                    self.cashier_row = col
                    self.cashiers[cell] = self.grid[row][col] = cash_register
                    self.cash_registers[cell] = (row, col)
                    self.queues[cell] = set()
                    # TODO: Add (remove) only upon cashier opening (closing)
                    self.schedule.add(cash_register)

                    cashier = CashierAgent('Y{}'.format(cell), self, (row + 1, col), cash_register)
                    self.grid[row + 1][col] = cashier
                elif cell in ['A', 'B', 'C', 'D', 'E']:
                    self.entry_points.append((row, col, cell))
                    self.spawn_row = col

        self.lane_switch_boundary = math.ceil((self.cashier_row - self.spawn_row) * 3 / 4)

        self.heatmap = np.zeros((self.height, self.width))

        world_matrix = np.matrix(self.world)
        self.distance_matrix = np.zeros((self.height, self.width))
        self.distance_matrix[world_matrix == 'X'] = np.inf
        self.distance_matrix[world_matrix == '1'] = np.inf
        self.distance_matrix[world_matrix == '2'] = np.inf
        self.distance_matrix[world_matrix == '3'] = np.inf
        self.distance_matrix[world_matrix == '4'] = np.inf
        self.distance_matrix[world_matrix == '5'] = np.inf
        self.distance_matrix[world_matrix == 'Y'] = np.inf

        self.floor_fields = {}
        for dest_label, (dest_col, dest_row) in self.cash_registers.items():
            floor_field = self.calculate_floor_field((dest_row, dest_col - 1))

            self.floor_fields[dest_label] = floor_field.copy()

            # Save floor field heatmap into file
            # floor_field[floor_field == np.inf] = -np.inf
            # plt.figure(figsize=(14, 14))
            # sns.heatmap(floor_field, vmin=0, fmt='.1f', vmax=np.max(floor_field), annot=True, cbar=False, square=True, cmap='mako', xticklabels=False, yticklabels=False)
            # plt.tight_layout()
            # plt.savefig(os.path.join('..', 'output', 'ff-heatmap{}.png'.format(dest_label)))
            # plt.close()

        self.datacollector = DataCollector(
            model_reporters={"Total": get_total_agents,
                             "Shopping": get_shopping_agents,
                             "Queued": get_queued_agents,
                             "Queued (AVG)": get_avg_queued_agents,
                             "Queued Time (AVG)": get_avg_queued_steps,
                             "Total Time (AVG)": get_avg_total_steps,
                             "Paying": get_paying_agents})

        if self.queue_type == QueueType.SNAKE:
            self.distance_matrix[world_matrix == 'X'] = 1
            self.distance_matrix[world_matrix == '1'] = 1
            self.distance_matrix[world_matrix == '2'] = 1
            self.distance_matrix[world_matrix == '3'] = 1
            self.distance_matrix[world_matrix == '4'] = 1
            self.distance_matrix[world_matrix == '5'] = 1
            self.distance_matrix[world_matrix == 'Y'] = 1
            self.movement_grid = Grid(matrix=self.distance_matrix, inverse=True)

        coin = self.random.randint(1, len(self.cashiers))
        self.cashiers[str(coin)].set_life()

        coin = self.random.randint(1, len(self.cashiers))
        while self.cashiers[str(coin)].open:
            coin = self.random.randint(1, len(self.cashiers))

        self.cashiers[str(coin)].set_life()

    def step(self):
        self.current_agents = len(self.schedule.agents) - len(self.cashiers.items())

        if self.schedule.steps > self.steps_in_day and get_total_agents(self) == 0 and (self.schedule.steps - 3) % 250:
            self.store_heatmap()
            self.running = False
            return

        if self.schedule.steps < self.steps_in_day and self.steps_in_day and self.schedule.steps % 25 == 0 and self.current_agents < self.capacity and self.should_spawn_agent():
            self.schedule.add(self.create_agent())

        self.datacollector.collect(self)
        self.schedule.step()

        self.adjust_cashiers()
        if self.queue_type == QueueType.SNAKE:
            self.assign_cash_register_to_customer()

    def adjust_cashiers(self):
        # self.current_agents > (len(opened) + 1) * self.capacity / 7
        # (len(opened) + 1) * self.queue_length_limit / self.current_agents

        opened, closed = self.partition(self.cashiers.values(), lambda c: c.open)
        if len(closed) > 0:
            if len(opened) < self.ideal_number_of_cashier(self.schedule.steps) and self.current_agents > (len(opened) + 1) * self.queue_length_limit:
                coin = self.random.randint(0, len(closed) - 1)
                cashier = closed[coin]
                cashier.set_life()
                self.open_cashier.add(cashier.unique_id)
                print(Back.WHITE + Fore.GREEN + 'OPENING NEW CASH_REGISTER: {}'.format(cashier.unique_id))

        if len(opened) > 2:
            np.random.shuffle(opened)
            if self.queue_type == QueueType.CLASSIC:
                for cashier in opened:
                    in_queue = len(self.queues[cashier.unique_id])
                    if (in_queue > 1 or in_queue == 0) and len(opened) > self.ideal_number_of_cashier(self.schedule.steps) and self.current_agents < (len(opened) + 1) * self.queue_length_limit:
                        self.close_cashier(cashier)
                        opened.remove(cashier)
                        break

            elif self.queue_type == QueueType.SNAKE:
                if len(opened) > self.ideal_number_of_cashier(self.schedule.steps) and self.current_agents < (len(opened) + 1) * self.queue_length_limit:
                    to_close = opened
                    if len(to_close) > 0:
                        coin = self.random.randint(0, len(to_close) - 1)
                        cashier = to_close[coin]

                        self.close_cashier(cashier)
                        opened.remove(cashier)
                        [cashier.set_life(cashier.remaining_life + 25) for cashier in opened]

    def assign_cash_register_to_customer(self):
        available, busy = self.partition(self.cashiers.values(), lambda c: c.open and not c.is_busy)
        if (not self.grid.is_cell_empty(self.snake_exit)) and len(available) > 0:
            customer = self.grid.get_cell_list_contents(self.snake_exit)[0]

            coin = self.random.randint(0, len(available) - 1)
            cashier = available[coin]

            customer.objective = cashier.unique_id
            dest_col, dest_row = cashier.pos
            customer.destination = (dest_col - 1, dest_row)
            cashier.is_busy = True
            print(Back.WHITE + Fore.BLACK + 'ASSIGNING CASH_REGISTER {} TO CUSTOMER {}'.format(coin, customer.unique_id))

    def store_heatmap(self):
        self.heatmap /= np.max(self.heatmap)
        sns.heatmap(self.heatmap, vmin=0, vmax=1)
        plt.savefig(os.path.join('..', 'output', 'heatmap{}.png'.format('' if self.queue_type == QueueType.CLASSIC else '-snake')))
        plt.close()

    def close_cashier(self, cashier):
        cashier.open = False
        cashier.empty_since = 0
        self.open_cashier.remove(cashier.unique_id)
        print(Back.WHITE + Fore.RED + 'CLOSING CASH_REGISTER: {}'.format(cashier.unique_id))

    def partition(self, elements, predicate):
        left, right = [], []
        for e in elements:
            (left if predicate(e) else right).append(e)

        return left, right

    def create_agent(self):
        agent = CustomerAgent(self.generated_customers_count, self, self.random_sprite())
        self.generated_customers_count += 1
        return agent

    def should_spawn_agent(self):
        relative_time = self.schedule.steps % self.steps_in_day
        prob = (-math.cos(relative_time * np.pi / (self.steps_in_day / 2) + 1) + 1) / 2
        return self.random.random() <= 0.85 if self.random.random() <= prob else False

    def ideal_number_of_cashier(self, step):
        prob = (step % self.steps_in_day) / self.steps_in_day

        # if prob <= 0.125 or prob >= 0.875:
        #     return 2
        # if prob <= 0.25 or prob >= 0.75:
        #     return 3
        # if prob <= 0.375 or prob >= 0.625:
        #     return 4
        # if prob <= 0.75 or prob >= 0.25:
        #     return 5

        if prob <= 0.265 or prob >= 0.88:
            return 2
        if prob <= 0.36 or prob >= 0.765:
            return 3
        if prob <= 0.44 or prob >= 0.66:
            return 4

        return 5

    def random_sprite(self):
        sprites = [
            'images/characters/grandpa3',
            'images/characters/man5',
            'images/characters/man8',
            'images/characters/girl',
            'images/characters/girl3',
            'images/characters/girl9',
        ]

        return sprites[self.random.randint(0, len(sprites) - 1)]

    def calculate_floor_field(self, destination):
        field = self.distance_matrix.copy()

        for row in range(len(field)):
            for col in range(len(field[row])):
                if not np.isinf(field[row, col]):
                    field[row, col] = distance.euclidean([row, col], destination)

        return field
Пример #17
0
class Factory(Model):
    """The Factory model that maintains the state of the whole factory."""

    def __init__(self, grid_w, grid_h, n_robots):
        """Initialize factory."""
        # Initialize.
        self.orders = 0
        self.n_robots = n_robots
        self.scheduler = RandomActivation(self)
        self.grid = SingleGrid(grid_w, grid_h, torus=False)
        self.init_astar()
        # Initialize departments.
        self.machine = Machine("machine", self, self.grid.find_empty())
        self.store = Store("store", self, self.grid.find_empty())
        self.packaging = Packaging("packaging", self, self.grid.find_empty())
        self.dept_positions = [self.machine.pos, self.store.pos, self.packaging.pos]
        # Initialize robots.
        for i in range(self.n_robots):
            # Create robot.
            r = Robot(i, self)
            # Initialize random location.
            pos = self.grid.find_empty()
            self.grid.place_agent(r, pos)
            # Register with scheduler.
            self.scheduler.add(r)
        # Initialize visualization.
        plt.ion()

    def add_order(self):
        """Increment the number of orders to the factory."""
        self.orders += 1

    def step(self):
        """Advance the factory by one step."""
        # Step through factory. Check for orders.
        if self.orders > 0:
            self.store.orders += 1
            self.orders -= 1
        # Step through departments.
        self.store.step()
        self.machine.step()
        self.packaging.step()
        # Step through robots.
        self.scheduler.step()
        # Visualize.
        self.visualize()

    def init_astar(self):
        """Initialize a-star resources so that it doesn't have to calculated for each robot.

        Initialized in such a way that:
            * A diagonal paths are allowed.
            * The path calculated takes into account all obstacles in the grid.
        """
        def get_empty_neighborhood(pos):
            """A sub function to calculate empty neighbors of a point for a-star."""
            neighbors = self.grid.get_neighborhood(pos=pos, moore=True)
            return [n for n in neighbors if self.grid.is_cell_empty(n)]
        # Initialize a path finder object once for the entire factory.
        self.path_finder = astar.pathfinder(neighbors=get_empty_neighborhood,
                                            distance=astar.absolute_distance,
                                            cost=astar.fixed_cost(1))

    def find_nearest_aimless_robot(self, pos):
        """Find the nearest aimless robot to a given position in the factory."""
        def is_aimless(robot, pos):
            """Check if the robot satisfied aimless condition."""
            if robot.destination is None:
                return True
            else:
                return False

        aimless_robots = [robot for robot in self.scheduler.agents if is_aimless(robot, pos)]
        if len(aimless_robots) != 0:
            robot_distances = [astar.absolute_distance(pos, robot.pos) for robot in aimless_robots]
            nearest_index = np.argmin(robot_distances)
            return aimless_robots[nearest_index]
        else:
            return None

    def find_robot_at_position(self, pos):
        """Find robot that is at a given location in the factory that is not busy."""
        for robot in self.scheduler.agents:
            if robot.pos == pos:
                return robot
        return None

    def find_next_position_towards_destination(self, curr_pos, dest_pos):
        """Find the next empty position to move in the direction of the destination."""
        n_steps, path = self.path_finder(curr_pos, dest_pos)  # Handles non-empty locations.
        # NOTE: We cannot find a valid path to the destination when:
        #   1) The destination has an another robot located inside it, which also occurs when curr_pos and
        #       dest_pos are the same.
        #   2) The path is entirely blocked.
        #   In these cases we return the next position to be the curr_pos, in order to wait until things
        #   clear up.
        if n_steps is None or n_steps <= 0:  # No valid path to destination
            next_pos = curr_pos
            print("[MOVE] Warning: No path to destination from {} --> {}".format(curr_pos, dest_pos))
        # This mean there's a valid path to destination.
        else:
            # index 0, is the curr_pos, index 1 is the next position.
            next_pos = path[1]
        return next_pos

    def find_next_position_for_random_walk(self, curr_pos):
        """Find a valid location for a robot to just randomly walk into."""
        def is_pos_empty(pos):
            """A sub function if a cell is empty for random walking."""
            if self.grid.is_cell_empty(pos) and pos not in self.dept_positions:
                return True
            else:
                return False
        neighborhood = self.grid.get_neighborhood(curr_pos, moore=True)
        empty_neighborhood = [n for n in neighborhood if is_pos_empty(n)]
        if len(empty_neighborhood) > 0:
            next_index = np.random.randint(len(empty_neighborhood))
            next_pos = empty_neighborhood[next_index]
        else:
            next_pos = curr_pos
        return next_pos

    def visualize(self):
        """A chess board type visualization."""
        def heatmap(a):
            cMap = ListedColormap(['grey', 'black', 'green', 'orange', 'red', 'blue'])
            sns.heatmap(a, vmin=0, vmax=6, cmap=cMap, linewidths=1)
            plt.pause(0.15)
            plt.clf()

        g = np.zeros((self.grid.height, self.grid.width), dtype=int)
        g[self.store.pos] = 3
        g[self.machine.pos] = 4
        g[self.packaging.pos] = 5
        for robot in self.scheduler.agents:
            if robot.destination is None:
                g[robot.pos] = 1
            else:
                g[robot.pos] = 2

        heatmap(g)
Пример #18
0
class Anthill(Model):
    def __init__(self):

        self.grid = SingleGrid(WIDTH, HEIGHT, False)
        self.schedule = RandomActivation(self)
        self.running = True
        self.internalrate = 0.2
        self.ant_id = 1
        self.tau = np.zeros((WIDTH,HEIGHT))
        self.datacollector = DataCollector({"Total number of Ants": lambda m: self.get_total_ants_number(),
                                            "mean tau": lambda m: self.evaluation1(),
                                            "sigma": lambda m: self.evaluation2(),
                                            "sigma*" :  lambda m: self.evaluation3(),
                                            })

        # List containing all coordinates of the boundary, initial ants location and brood location
        self.bound_vals = []
        self.neigh_bound = []
        self.middle=[]
        self.datacollector.collect(self)
        self.passage_to_right = []
        self.passage_to_left = []

        # for i in range(WIDTH):
        #     for j in range(HEIGHT):
        #         if i == 0 or j == 0 or i == WIDTH-1 or j == HEIGHT-1:
        #             self.bound_vals.append((i,j))
        #         if i == 1 or i == WIDTH - 2 or j == 1 or j == HEIGHT-2:
        #             self.neigh_bound.append((i,j))
        ##

        for i in range(WIDTH):
            for j in range(HEIGHT):
                ##make boundary
                if i == 0 or j == 0 or i == WIDTH - 1 or j == HEIGHT - 1:
                    self.bound_vals.append((i,j))
                if i == MIDDLE and 0<j<WIDTH - 1:
                    self.bound_vals.append((i,j))
                    self.middle.append((i,j))

                ##save neighbor
                if j ==1 and 1<= i <= MIDDLE-2:
                    self.neigh_bound.append((i,j))
                if j ==1 and MIDDLE+2<=i<=WIDTH - 2:
                    self.neigh_bound.append((i,j))
                if j ==HEIGHT - 1 and 1<= i <= MIDDLE-2:
                    self.neigh_bound.append((i,j))
                if j ==HEIGHT - 1 and MIDDLE+2<=i<=WIDTH - 2:
                    self.neigh_bound.append((i,j))
                if i == 1 and 2<= j<= MIDDLE-3:
                    self.neigh_bound.append((i, j))
                if i == HEIGHT - 2 and 2<= j<= MIDDLE-3:
                    self.neigh_bound.append((i, j))

                ## we let the columns next to th middle become the entrance to next chamber
                if i == MIDDLE-1 and 0<j<WIDTH-1:
                    self.passage_to_left.append((i, j))
                if i == MIDDLE + 1 and 0 < j < WIDTH - 1:
                    self.passage_to_right.append((i, j))




        # Make a Fence boundary
        b = 0
        for h in self.bound_vals:
            br = Fence(b,self)

            self.grid.place_agent(br,(h[0],h[1]))
            b += 1



    def step(self):
        '''Advance the model by one step.'''

        # Add new ants into the internal area ont he boundary
        for xy in self.neigh_bound:
            # Add with probability internal rate and if the cell is empty
            if self.random.uniform(0, 1) < self.internalrate and self.grid.is_cell_empty(xy) == True:

                a = Ant(self.ant_id, self)

                self.schedule.add(a)
                self.grid.place_agent(a,xy)

                self.ant_id += 1

        # Move the ants
        self.schedule.step()
        self.datacollector.collect(self)

        # with open("data/p02_b0_tau.txt", "a") as myfile:
        #     myfile.write(str(self.mean_tau_ant) + '\n')
        # with open("data/p02_b0_sigma.txt", "a") as myfile:
        #     myfile.write(str(self.sigma) + '\n')
        # with open("data/p02_b0_sigmastar.txt","a") as myfile:
        #     myfile.write(str(self.sigmastar) + "\n")

    def get_total_ants_number(self):
        total_ants=0
        for (agents, _, _) in self.grid.coord_iter():
            if type(agents) is Ant:
                total_ants += 1
        return total_ants

    def evaluation1(self):
        ##creat a empty grid to store currently information
        total_ants = np.zeros((WIDTH,HEIGHT))

        ## count the number of currently information
        for (agents, i, j) in self.grid.coord_iter():
            if type(agents) is Ant:
                total_ants[i][j] = 1
            else:
                total_ants[i][j] = 0
        ##update the tau
        self.tau = self.tau + total_ants
        ##calcualte the mean tau
        self.mean_tau_ant = self.tau.sum()/((WIDTH-2)**2)
        return self.mean_tau_ant

    def evaluation2(self):


        ## we need to minus the mean tau so we need to ensure the result of boundary is zero
        ## so we let the bounday equal mean_tau_ant in this way the (tau-mean_tau_ant) is zero of boundary
        for site in self.bound_vals:
            self.tau[site[0]][site[1]] = self.mean_tau_ant


        ##calculate the sigmaa
        self.sigma = ((self.tau-self.mean_tau_ant)**2).sum()/((WIDTH-2)**2)

        ## rechange the boundaryy
        for site in self.bound_vals:
            self.tau[site[0]][site[1]] = 0

        return np.sqrt(self.sigma)

    def evaluation3(self):
        ##calculate the sigmastar
        self.sigmastar = np.sqrt(self.sigma)/self.mean_tau_ant

        return self.sigmastar