Ejemplo n.º 1
0
class Population(Model):
    """Population
    Adapted from https://www.medrxiv.org/content/10.1101/2020.03.18.20037994v1.full.pdf

    Model Parameters:
    spread_chance: probability of infection based on contact
    gamma: mean incubation period
    alpha: probability of become asymptomatic vs symptomatic
    gamma_AR: infectious period for asymptomatic people
    gamma_YR: infectious period for symptomatic people
    delta: death rate due to disease

    The social distancing func takes time passed -> new interaction multiplier
    """
    def __init__(self,
                 graph,
                 model_parameters,
                 social_distancing_func=lambda x: 1):

        # Model initialization
        self.population_size = model_parameters['population size']
        self.initial_outbreak_size = model_parameters['initial outbreak size']
        self.graph = graph
        self.grid = NetworkGrid(self.graph)
        self.schedule = SimultaneousActivation(self)
        self.social_distancing_func = social_distancing_func

        self.datacollector = DataCollector({  # "Exposed": count_exposed,
            "Susceptible": count_susceptible,
            "Recovered": count_recovered,
            # "Asymptomatic": count_asymptomatic,
            # "Symptomatic": count_symptomatic,
            "Diseased": count_diseased,
            "Removed": count_removed
        })
        self.model_parameters = model_parameters

        for i, node in enumerate(self.graph.nodes()):
            a = Person(i, self, State.SUSCEPTIBLE, model_parameters,
                       social_distancing_func)
            self.schedule.add(a)
            self.grid.place_agent(a, i)
            if i % 100 == 0:
                logger.info("Finished with agent " + str(i))

        infected_nodes = self.random.sample(self.graph.nodes(),
                                            self.initial_outbreak_size)
        for a in self.grid.get_cell_list_contents(infected_nodes):
            a.status = State.EXPOSED

        self.datacollector.collect(self)
        print("Model initialized...\n")

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

    def run(self, n):
        for i in range(n):
            logger.info("Steps Completed: " + str(i))
            self.step()

    def run_until_stable(self):
        max_steps = 1e3
        steps = 0
        window_size = 50
        while steps < max_steps:
            self.step()
            logger.info("Steps Completed:" + str(steps))
            if steps > window_size:
                data = self.get_data()
                last_value = int(data.tail(1)['Diseased'])
                if last_value == 0:
                    break
                window_average = np.mean(
                    data.tail(window_size)
                    ['Diseased'])  # Window for determining stopping rule
                if abs(last_value - window_average) / window_average < 0.005:
                    break
            steps += 1

    def cross_influence(self, influence_coefficients, ratios):
        for inf_coeff, ratio in zip(influence_coefficients, ratios):
            susceptibles = list(
                filter(lambda x: x.status == State.SUSCEPTIBLE,
                       self.grid.get_all_cell_contents()))
            to_flip = self.random.sample(
                susceptibles, int(inf_coeff * ratio * len(susceptibles)))
            for agent in to_flip:
                agent.status = State.EXPOSED

    def clear_social_distancing_func(self):
        """
        Clears the social distancing function of the model and all its agents for pickling
        :return: None
        """
        self.social_distancing_func = None
        for agent in self.grid.get_all_cell_contents():
            agent.social_distancing_func = None

    def reinstate_social_distancing_func(self,
                                         social_distancing_func=lambda x: 1):
        """
        Re-adds the social distancing func to the model and all its agents
        :param social_distancing_func: social distancing func to be re-added
        :return: None
        """
        self.social_distancing_func = social_distancing_func
        for agent in self.grid.get_all_cell_contents():
            agent.social_distancing_func = social_distancing_func

    def save_model(self, filename):
        """
        Save the model to a pickle and dill file
        :param filename: filename (without extension) to save to
        :return: None
        """
        with open(filename + ".dil", 'wb') as f:
            dill.dump(self.social_distancing_func, f)
        self.clear_social_distancing_func()
        with open(filename + ".pkl", 'wb') as f:
            pickle.dump(self, f)

    def get_data(self):
        return self.datacollector.get_model_vars_dataframe()

    '''
Ejemplo n.º 2
0
class Themepark(Model):
    def __init__(self, N_attr, N_cust, width, height, strategy, theme,
                 max_time, weight, adaptive):
        self.max_time = max_time
        self.N_attr = N_attr
        self.penalty_per = PENALTY_PERCENTAGE
        self.weight = weight
        self.adaptive = adaptive
        self.strategies = STRATEGIES
        self.x_list, self.y_list, self.positions = xlist, ylist, positions
        self.x_list, self.y_list, self.positions = get_attraction_coordinates(
            WIDTH, HEIGHT, self.N_attr, theme)
        self.happinesses = []
        self.path_coordinates = get_coordinates(WIDTH, HEIGHT, NUM_OBSTACLES,
                                                self.N_attr, theme)
        self.N_cust = N_cust  # num of customer agents
        self.total_steps = 0
        self.cust_ids = N_cust
        self.strategy = strategy
        self.grid = MultiGrid(width, height, torus=False)
        self.schedule = BaseScheduler(self)
        self.schedule_Attraction = BaseScheduler(self)
        self.schedule_Customer = BaseScheduler(self)
        self.totalTOTAL = 0  #TODO: DEZE NAAM VERANDEREN
        self.attractions = self.make_attractions()
        self.attraction_history = self.make_attr_hist()
        self.park_score = []
        self.data_dict = {}
        self.hist_random_strat = []
        self.hist_close_strat = []
        self.all_rides_list = []
        self.strategy_composition = self.make_strategy_composition()
        self.customers = self.add_customers(self.N_cust)
        self.monitor = Monitor(self.max_time, self.N_attr, self.positions)
        self.running = True

        # TODO: ALS HET GOED IS KUNNEN AL DEZE INITS WEG, MAAR DIT MOETEN WE WEL NOG EVEN DUBBEL CHECKEN
        # --> dit is gecheckt op het runnen van main_cluster_random_noise
        # self.theme = theme
        # self.starting_positions = [[int((WIDTH/2)-1), 0], [int(WIDTH/2), 0], [int((WIDTH/2)+1), 0]]
        # self.width = width
        # self.height = height
        # self.data = []
        # self.data_customers = []
        # self.memory = 5
        # self.customer_score = []
        # self.only_random = False
        # self.total_waited_time = 0

        # Initialize dictionary of attractions
        for attraction in self.get_attractions():
            self.data_dict[attraction.unique_id] = ({
                "id": attraction.unique_id,
                "length": attraction.attraction_duration,
                "waiting_list": []
            })

        # TODO ADD COMMENT (SNAP NIET WAT DE DATACOLLECTOR IS)
        if len(self.strategies) == 6:
            self.datacollector = DataCollector({
                "Random":
                lambda m: self.strategy_counter(self.strategies[0]),
                "0.00":
                lambda m: self.strategy_counter(self.strategies[1]),
                "0.25":
                lambda m: self.strategy_counter(self.strategies[2]),
                "0.50":
                lambda m: self.strategy_counter(self.strategies[3]),
                "0.75":
                lambda m: self.strategy_counter(self.strategies[4]),
                "1.00":
                lambda m: self.strategy_counter(self.strategies[5]),
            })
        else:
            self.datacollector = DataCollector({
                "0.00":
                lambda m: self.strategy_counter(self.strategies[0]),
                "0.25":
                lambda m: self.strategy_counter(self.strategies[1]),
                "0.50":
                lambda m: self.strategy_counter(self.strategies[2]),
                "0.75":
                lambda m: self.strategy_counter(self.strategies[3]),
                "1.00":
                lambda m: self.strategy_counter(self.strategies[4]),
            })

        self.datacollector2 = DataCollector(
            {"score": lambda m: self.make_score()})

    def make_score(self):
        """
        Get the efficiency score
        """
        ideal = {}
        cust_in_row = 0
        for i in range(len(self.get_attractions())):
            ideal[i] = self.N_cust / self.N_attr
            cust_in_row += self.get_attractions()[i].N_current_cust

        tot_difference = 0
        for i in range(len(self.get_attractions())):

            difference = abs(cust_in_row / self.N_attr -
                             self.get_attractions()[i].N_current_cust)
            tot_difference += difference

        fraction_not_right = (tot_difference / self.N_cust)
        return abs(1 - (fraction_not_right)) * cust_in_row / self.N_cust

    def make_attr_hist(self):
        """
        Initialize a dictionary in which the history of the attractions can be
        added in.
        """
        attraction_history = {}
        for attraction in self.get_attractions():
            attraction_history[attraction] = [0] * (self.max_time + 1)

        return attraction_history

    def strategy_counter(self, strategy):
        """
        Count how many customers of different strategies are at the attractions
        """
        counter_total = {}

        for attraction_pos in self.positions:

            agents = self.grid.get_neighbors(attraction_pos,
                                             moore=True,
                                             radius=0,
                                             include_center=True)

            counter = 0
            for agent in self.customers:
                if agent.weight == strategy:
                    counter += 1

        return counter

    def make_strategy_composition(self):
        """
        TODO: ANNEMIJN KAN JIJ HIER COMMENTS BIJ DOEN? + RANDOM_TEST_4 WEGHALEN
        """
        if self.strategy == "Random_test_4":
            self.strategies = ["Random_test_4", 0.0, 0.25, 0.50, 0.75, 1.0]
            dict = {
                self.strategies[0]: 1 / 6,
                self.strategies[1]: 0.20,
                self.strategies[2]: 0.20,
                self.strategies[3]: 0.20,
                self.strategies[4]: 0.20,
                self.strategies[5]: 0.20
            }

            composition_list = []
            for i in range(len(self.strategies)):
                if i == 0:
                    dict[self.strategies[i]] = FRACTION_RANDOM
                    continue
                else:
                    composition_list.append(random.randint(0, 100))
            sum_comp = sum(composition_list)

            sum_comp = sum_comp - sum_comp * FRACTION_RANDOM
            for i in range(len(self.strategies)):
                if i == 0:
                    continue
                else:
                    dict[self.strategies[i]] = composition_list[i -
                                                                1] / sum_comp

        else:
            dict = {
                self.strategies[0]: 0.20,
                self.strategies[1]: 0.20,
                self.strategies[2]: 0.20,
                self.strategies[3]: 0.20,
                self.strategies[4]: 0.20
            }

            composition_list = []
            for i in range(len(self.strategies)):

                composition_list.append(random.randint(0, 100))

            sum_comp = sum(composition_list)

            sum_comp = sum_comp
            for i in range(len(self.strategies)):

                dict[self.strategies[i]] = composition_list[i - 1] / sum_comp

        return dict

    def make_attractions(self):
        """
        Initialize attractions on fixed position.
        """

        attractions = {}
        for i in range(self.N_attr):

            pos = (self.x_list[i], self.y_list[i])
            if self.grid.is_cell_empty(pos):

                name = str(i)
                a = Attraction(i, self, pos, name, self.N_cust, self.weight)
                attractions[i] = a

                self.schedule_Attraction.add(a)
                self.grid.place_agent(a, pos)
        return attractions

    def get_attractions(self):
        """
        Get a list with all attractions.
        """
        agents = self.grid.get_neighbors(mid_point,
                                         moore=True,
                                         radius=RADIUS,
                                         include_center=True)

        attractions = []
        for agent in agents:
            if type(agent) == Attraction:
                attractions.append(agent)

        return attractions

    def calculate_people(self):
        """
        Calculate how many customers are in which attraction.
        """

        counter_total = {}

        for attraction_pos in self.positions:

            agents = self.grid.get_neighbors(attraction_pos,
                                             moore=True,
                                             radius=0,
                                             include_center=True)

            counter = 0
            for agent in agents:
                if type(agent) is Customer:
                    counter += 1
                else:
                    attraction = agent

            attraction.N_current_cust = counter
            counter_total[attraction.unique_id] = counter

        return list(counter_total.values())

    def add_customers(self, N_cust, added=False):
        """
        Initialize customers on random positions.
        """

        weights_list = []
        if self.adaptive is True:

            for j in self.strategy_composition.keys():
                for i in range(round(N_cust * self.strategy_composition[j])):
                    weights_list.append(j)

            if len(weights_list) < self.N_cust:
                rand = random.choice(self.strategies)
                weights_list.append(rand)
            elif len(weights_list) > self.N_cust:
                rand = random.choice(weights_list)
                weights_list.remove(rand)

        else:

            # if the strategy is not random add weights to weights_list
            if self.strategy is not "Random":
                for i in range(round(N_cust)):
                    weights_list.append(self.weight)

        cust_list = []
        for i in range(N_cust):

            pos_temp = [
                random.randint(0, WIDTH - 1),
                random.randint(0, HEIGHT - 1)
            ]
            rand_x, rand_y = pos_temp[0], pos_temp[1]

            pos = (rand_x, rand_y)

            if added is True:
                i = self.cust_ids
            if self.strategy == "Random_test_4":
                if weights_list[i] == "Random_test_4":
                    strategy = "Random_test_4"
                else:
                    strategy = "Closest_by"
            else:
                strategy = self.strategy

            # Set weight
            if weights_list == []:
                weight = None
            else:
                weight = weights_list[i]

            # Initialize customer and add customer to the model
            a = Customer(i, self, pos, self.x_list, self.y_list,
                         self.positions, strategy, weight, self.adaptive)
            self.schedule_Customer.add(a)
            self.grid.place_agent(a, pos)
            cust_list.append(a)

        return cust_list

    def calc_waiting_time(self):
        """
        Calculate the waitingtime per attraction
        """

        counter_total = {}

        attractions = self.get_attractions()
        for attraction in attractions:

            counter_total[
                attraction.unique_id] = attraction.current_waitingtime

        return counter_total

    def calculate_people_sorted(self):
        """
        Calculate how many customers are in which attraction.
        Returns a SORTED LIST.
        For example: indexes = [3, 2, 5, 1, 4]
        indicates that attraction3 has the least people waiting.
        """

        counter_total = {}

        for attraction_pos in self.positions:

            agents = self.grid.get_neighbors(attraction_pos,
                                             moore=True,
                                             radius=0,
                                             include_center=True)

            counter = 0
            for agent in agents:
                if type(agent) is Customer:
                    counter += 1
                else:
                    attraction = agent

            attraction.N_current_cust = counter
            self.attraction_history[attraction][self.totalTOTAL] = counter
            counter_total[attraction.unique_id] = counter

        return counter_total

    def make_route(self):
        """
        Draw coordinates of a possible path.
        """

        for i in range(len(self.path_coordinates)):
            pos = self.path_coordinates[i]

            if pos not in self.positions:

                # Create path agent
                path = Route(i, self, pos)
                self.schedule.add(path)

                self.grid.place_agent(path, pos)

    # TODO: THEMEPARK SCORE GEBRUIKEN WE NIET MEER, DIT ALLEMAAL VERWIJDEREN OVERAL?
    def get_themepark_score(self):
        """
        Get score of a themepark based on:
            - A total of all waitingtimes for every customer
            - The total number of rides taken
        """
        attractions = self.get_attractions()
        total_wait, total_rides = 0, 0
        for attraction in attractions:
            total_wait += attraction.current_waitingtime

            if attraction.current_a is not None:
                total_rides += 1

        if total_rides == 0:
            return total_rides

        return (total_wait / total_rides)

    def get_strategy_history(self):
        """
        Update history with how many customers chose which strategy.
        """

        customers = self.get_customers()
        randomstrat, closebystrat = 0, 0

        for customer in customers:
            if customer.strategy == "Random" or customer.strategy == "Random_test_4":
                randomstrat += 1
            elif customer.strategy == "Closest_by":
                closebystrat += 1

        self.hist_random_strat.append(randomstrat)
        self.hist_close_strat.append(closebystrat)

    def get_customers(self):
        """
        Returns a list of all the customers in the themepark.
        """
        agents = self.grid.get_neighbors(mid_point,
                                         moore=True,
                                         radius=RADIUS,
                                         include_center=True)

        customers = []

        # Add customers to list
        for agent in agents:
            if type(agent) == Customer:
                customers.append(agent)

        return customers

    def get_data_customers(self):
        """
        Return dictionary with data of customers.
        """

        data = {}
        agents = self.grid.get_neighbors(mid_point,
                                         moore=True,
                                         radius=RADIUS,
                                         include_center=True)

        for agent in agents:
            if type(agent) is Customer:
                data[agent.unique_id] = {
                    "totalwaited": agent.total_ever_waited,
                    "visited_attractions": agent.nmbr_attractions,
                    "strategy": agent.strategy,
                    "swapped_strat": agent.strategy_swap_hist
                }

        return data

# TODO: NET ALS THEMEPARK SCORE GEBRUIKEN WE DIT NEIT MEER TOCH????

    def calc_hapiness(self):
        """
        Calculate mean hapiness of all customers, based on:

        - How many rides were taken
        - Number of times in the same attraction
        - Total waiting time
        """
        customers = self.get_customers()

        scores = []

        for customer in customers:
            history = customer.history
            values = list(history.values())
            total_rides = sum(values)

            if total_rides != 0:
                scores.append(total_rides / self.N_attr -
                              self.totalTOTAL / customer.total_ever_waited)
            else:
                return None

        scores = np.interp(scores, (min(scores), max(scores)), (1, 10))

        return np.mean(scores)

    def get_history_list(self):
        """
        Create a list with the history of the customers.
        """

        customers = self.get_customers()
        histories = {}

        for customer in customers:
            history = customer.history
            values = list(history.values())
            histories[customer.unique_id] = values
        return histories

    def final(self):
        """
        End run and return data.
        """

        # Create list with at every time step the amount of attractions that are active
        attractions = self.get_attractions()
        self.all_rides_list = [0] * len(attractions[0].in_attraction_list)
        for attraction in attractions:
            for i in range(len(attraction.in_attraction_list)):
                self.all_rides_list[i] += attraction.in_attraction_list[i]

        # Change the all_rides_list into fractions
        for i in range(len(self.all_rides_list)):
            self.all_rides_list[i] /= self.N_attr

        # Initialize history list and get agents
        hist_list = []
        agents = self.grid.get_neighbors(mid_point,
                                         moore=True,
                                         radius=RADIUS,
                                         include_center=True)

        # Save history of all customers
        cust_data = self.get_data_customers()
        for agent in agents:
            if type(agent) is Customer:
                sum_attr = sum(agent.history.values())
                if sum_attr > 0:
                    hist_list.append(agent.strategy_swap_hist)
                else:
                    hist_list.append(agent.strategy_swap_hist)

        histories = self.get_history_list()

        # Save data
        try:
            pickle.dump(self.datacollector.get_model_vars_dataframe(),
                        open("../data/strategy_history.p", 'wb'))
            pickle.dump(self.datacollector2.get_model_vars_dataframe(),
                        open("../data/eff_score_history.p", 'wb'))
            pickle.dump(cust_data, open("../data/customers.p", 'wb'))
            pickle.dump(self.park_score[-1], open("../data/park_score.p",
                                                  "wb"))
            pickle.dump(self.happinesses, open("../data/hapiness.p", "wb"))
            pickle.dump(histories, open("../data/cust_history.p", 'wb'))
        except:
            pickle.dump(self.datacollector.get_model_vars_dataframe(),
                        open("data/strategy_history.p", 'wb'))
            pickle.dump(self.datacollector2.get_model_vars_dataframe(),
                        open("data/eff_score_history.p", 'wb'))
            pickle.dump(cust_data, open("data/customers.p", 'wb'))
            pickle.dump(self.park_score[-1], open("data/park_score.p", "wb"))
            pickle.dump(self.happinesses, open("data/hapiness.p", "wb"))
            pickle.dump(histories, open("data/cust_history.p", 'wb'))

        try:
            pickle.dump(self.all_rides_list, open("../data/all_rides.p", "wb"))
        except:
            pickle.dump(self.all_rides_list, open("data/all_rides.p", "wb"))

        print()
        print("RUN HAS ENDED")
        print()

    def save_data(self):
        """
        Save data of all attractions and customers.
        """
        # Get info
        waitinglines = self.calc_waiting_time()

        for i in range(len(self.attractions)):
            self.data_dict[i]["waiting_list"].append(waitinglines.get(i))

        self.park_score.append(sum(waitinglines.values()))
        self.happinesses.append(self.calc_hapiness())

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

        # Take a step if max steps is not reached
        if self.totalTOTAL < self.max_time:
            self.totalTOTAL += 1
            self.schedule.step()
            self.datacollector.collect(self)
            self.datacollector2.collect(self)

            self.schedule_Attraction.step()
            self.schedule_Customer.step()

            self.total_steps += 1

            self.save_data()
            self.get_strategy_history()

        # Stop simulation
        else:
            for key in self.attraction_history.keys():
                y = self.attraction_history[key]
                x = list(range(0, self.max_time))

            self.final()
Ejemplo n.º 3
0
class EcoModel(Model):
    '''
    Represents the landscape in the dryland model as a two-dimensional square grid
    '''
    def __init__(self, b, m, config_file):
        '''
            Create a new grid.

            Args:

            b: plant establishment probability.
            m: mortality probability of a vegetated site
            config_file: a .json file with remaining parameters
        '''
        # open json file with parameters
        params = json.load(open(config_file))

        # Initialize model variables
        self.height = params["height"]
        self.width = params["width"]
        self.num_agents = self.width * self.height
        self.schedule = SimultaneousActivation(self)
        self.delta = params["delta"]
        self.c = params["c"]
        self.r = params["r"]
        self.d = params["d"]
        self.f = params["f"]
        self.m = m
        self.b_base = b
        self.b = b
        self.emp_dens = params["Empty sites density"]
        self.deg_dens = params["Degraded sites density"]
        self.rho_veg = 1 - self.emp_dens - self.deg_dens

        # Set up flowlength parameters
        self.use_fl = params["Use Flowlength"]
        if self.use_fl:
            self.patch_size = params["Patch size"]  # patch side size in meters
            self.L = self.height
            self.theta = np.radians(params["Theta"])
            self.d_s = self.patch_size / np.cos(self.theta)
            self.max_fl = self.d_s * (self.height + 1) / 2
            self.alpha_feedback = params["alpha_feedback"]
            self.alpha_bare = 1
            q = self.alpha_bare * (1 - self.rho_veg)
            self.fl = (1 - self.rho_veg) * ((1 - q) * self.L - q *
                                            (1 - q**self.L)) * self.d_s / (
                                                (1 - q)**2 * self.L)
            self.b = self.b_base * (
                1 - self.alpha_feedback * self.fl / self.max_fl)

            # variables for infrequent rainfall
            self.infr_rain = params["Use infrequent rain"]
            if self.infr_rain:
                self.rain_period = params["Rain period"]
                self.no_rain_period = params["No rain period"]
                self.is_raining = True
                self.water_on = 0
                self.water_off = self.no_rain_period

        self.count_veg = int(self.rho_veg * self.num_agents)

        # Set up the model and data collection
        self.grid = Grid(self.height, self.width, torus=params["Use Torus"])
        if self.fl:
            self.datacollector = DataCollector({
                "Empty":
                lambda m: self.count_type(m, "Empty"),
                "Vegetated":
                lambda m: self.count_type(m, "Vegetated"),
                "Degraded":
                lambda m: self.count_type(m, "Degraded"),
                "qplusplus":
                lambda m: self.calculate_local_densities(m)[0],
                "qminusplus":
                lambda m: self.calculate_local_densities(m)[1],
                "qminusminus":
                lambda m: self.calculate_local_densities(m)[2],
                "flowlength":
                lambda m: self.fl,
                "b":
                lambda m: self.b
            })
        else:
            self.datacollector = DataCollector({
                "Empty":
                lambda m: self.count_type(m, "Empty"),
                "Vegetated":
                lambda m: self.count_type(m, "Vegetated"),
                "Degraded":
                lambda m: self.count_type(m, "Degraded"),
                "qplusplus":
                lambda m: self.calculate_local_densities(m)[0],
                "qminusplus":
                lambda m: self.calculate_local_densities(m)[1],
                "qminusminus":
                lambda m: self.calculate_local_densities(m)[2],
                "b":
                lambda m: self.b
            })

        # Define patches
        for x in range(self.width):
            for y in range(self.height):
                rand_num = random.random()
                if rand_num < self.deg_dens:
                    new_patch = Patch(self, (x, y), "Degraded")
                    self.grid[y][x] = new_patch
                    self.schedule.add(new_patch)
                elif rand_num < self.emp_dens + self.deg_dens:
                    new_patch = Patch(self, (x, y), "Empty")
                    self.grid[y][x] = new_patch
                    self.schedule.add(new_patch)
                else:
                    new_patch = Patch(self, (x, y),
                                      "Vegetated")  # Create a patch
                    self.grid[y][x] = new_patch
                    self.schedule.add(new_patch)

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

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

        # Calculate vegetation density
        self.count_veg = self.count_type(self, "Vegetated")
        self.rho_veg = self.count_veg / self.num_agents

        # Calculate Flowlength index
        if self.use_fl:

            # calculate probability of having neighbouring non-vegetated sites
            rho_minusminus = float(self.datacollector.get_model_vars_dataframe(
            ).qminusminus.tail(1)) * (1 - self.rho_veg)
            self.alpha_bare = rho_minusminus / (1 - self.rho_veg)**2
            q_flowlength = self.alpha_bare * (1 - self.rho_veg)

            # calculate Flowlength index based on the connectivity of non-vegetated patches
            self.fl = (1 - self.rho_veg) * ((1 - q_flowlength) * self.L - q_flowlength * (1 - q_flowlength ** self.L))\
                      * self.d_s / ((1 - q_flowlength) ** 2 * self.L)

            # get the establishment probability as the function of Flowlength and connectivity strength
            self.b = self.b_base * (
                1 - self.alpha_feedback * self.fl / self.max_fl)

            # calculations involving infrequent rain
            if self.infr_rain:
                if self.is_raining:
                    if self.water_on < self.rain_period - 1:
                        self.water_on += 1
                    else:
                        self.is_raining = False
                        self.water_off = 0
                        self.b = self.b_base
                else:
                    if self.water_off < self.no_rain_period - 1:
                        self.water_off += 1
                        self.b = self.b_base
                    else:
                        self.is_raining = True
                        self.water_on = 0

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

    @staticmethod
    def count_type(model, patch_condition):
        '''
        Helper method to count patches with a given condition in a given model
        '''

        count = 0
        for patch in model.schedule.agents:
            if patch.condition == patch_condition:
                count += 1
        return count

    @staticmethod
    def calculate_local_densities(model):
        '''
        Helper method to calculate global averages for conditional probabilities
        for having vegetated or degraded neighbors given the vegetated state,
        and for having non-vegetated neighbors given the non-vegetated state.
        (non-vegetated = empty or degraded)
        '''

        qplusplus = []
        qminusplus = []
        qminusminus = []
        for patch in model.schedule.agents:
            if patch.condition == "Vegetated":
                qplusplus.append(patch.get_q())
                qminusplus.append(patch.get_q_minus())
            else:
                qminusminus.append(patch.get_q_nonveg())

        return (np.mean(qplusplus), np.mean(qminusplus), np.mean(qminusminus))
class ExplorationArea(Model):
    def __init__(
            self,
            nrobots,
            wifi_range,
            radar_radius=6,
            alpha=8.175,
            gamma=0.65,
            inj_pri=0,
            ninjured=None,
            ncells=None,
            obstacles_dist=None,
            load_file=None,
            dump_datas=True,  # enable data collection
            alpha_variation=False,  # record datas for alpha variation studies
            alpha_csv=alpha_csv,  # aggregate datas
            alpha_step_csv=alpha_step_csv,  # single step datas
            gamma_variation=False,  # record datas for gamma variation studies
            gamma_csv=gamma_csv,
            optimization_task=False,  # enable a small part of data collection for optimization task
            time_csv=number_of_steps_csv,
            robot_status_csv=robot_status_csv):

        # checking params consistency
        if not load_file and (not ncells or not obstacles_dist
                              or not ninjured):
            print("Invalid params")
            sys.exit(-1)

        # used in server start
        self.running = True
        self.nrobots = nrobots
        self.radar_radius = radar_radius
        self.ncells = ncells
        self.obstacles_dist = obstacles_dist
        self.wifi_range = wifi_range
        self.alpha = alpha
        self.gamma = gamma
        self.ninjured = ninjured
        self.inj_pri = inj_pri
        self.dump_datas = dump_datas
        self.optimization_task = optimization_task
        self.frontier = set()
        self.broken_beans = 0
        # Data collection tools
        if self.dump_datas:
            # it represents the sum of the difficulties of every cell
            self.total_difficulty = 0

            self.dc_robot_status = DataCollector({
                "idling":
                lambda m: self.get_number_robots_status(m, "idling"),
                "travelling":
                lambda m: self.get_number_robots_status(m, "travelling"),
                "exploring":
                lambda m: self.get_number_robots_status(m, "exploring"),
                "deploying_bean":
                lambda m: self.get_number_robots_status(m, "deploying_bean"),
                "step":
                lambda m: self.get_step(m)
            })
            self.time_csv = time_csv
            self.robot_status_csv = robot_status_csv

        if self.optimization_task:
            self.total_idling_time = 0

        self.alpha_variation = alpha_variation
        self.gamma_variation = gamma_variation
        if self.alpha_variation:
            self.costs_each_path = list()
            self.alpha_csv = alpha_csv
            self.alpha_step = dict()
            self.alpha_step_csv = alpha_step_csv
        if self.gamma_variation:
            self.gamma_df = pd.DataFrame(columns=["step", "mean", "std"])
            self.gamma_csv = gamma_csv

        self.schedule = RandomActivation(self)
        # unique counter for agents
        self.agent_counter = 1
        self.nobstacle = 0
        # graph of seen cells
        self.seen_graph = nx.DiGraph()

        rnd.seed()

        # place a cell agent for store data and visualization on each cell of the grid
        # if map is not taken from file, create it
        if load_file == None:
            self.grid = MultiGrid(ncells + 2, ncells + 2, torus=False)
            for i in self.grid.coord_iter():
                if i[1] != 0 and i[2] != 0 and i[1] != self.ncells + 1 and i[
                        2] != self.ncells + 1:
                    rand = np.random.random_sample()
                    obstacle = True if rand < self.obstacles_dist else False
                    # if obstacle
                    if obstacle:
                        self.nobstacle += 1
                        difficulty = math.inf
                        explored = -1
                        priority = 0
                        utility = -math.inf
                    # if free
                    else:
                        difficulty = np.random.randint(low=1, high=13)
                        if self.dump_datas:
                            self.total_difficulty += difficulty
                        explored = 0
                        priority = 0
                        utility = 1.0
                # if contour cell
                else:
                    difficulty = np.random.randint(low=1, high=13)
                    explored = -2
                    priority = -math.inf
                    utility = -math.inf
                # place the agent in the grid
                a = Cell(self.agent_counter, self, i[1:], difficulty, explored,
                         priority, utility)
                self.grid.place_agent(a, i[1:])
                self.agent_counter += 1

            # create injured agents
            valid_coord = []
            for i in self.grid.coord_iter():
                cell = [
                    e for e in self.grid.get_cell_list_contents(i[1:])
                    if isinstance(e, Cell)
                ][0]
                if cell.explored == 0:
                    valid_coord.append(cell.pos)
            for i in range(0, ninjured):
                inj_index = rnd.choice(valid_coord)
                a = Injured(self.agent_counter, self, inj_index)
                self.schedule.add(a)
                self.grid.place_agent(a, inj_index)
                self.agent_counter += 1
        else:
            # load map from file
            try:
                with open(load_file, 'r') as f:
                    file = f.read()
            except:
                print("file not found")
                sys.exit(-1)
            exported_map = literal_eval(file)
            self.ncells = int(math.sqrt(len(exported_map["Cell"].keys()))) - 2
            self.grid = MultiGrid(self.ncells + 2,
                                  self.ncells + 2,
                                  torus=False)
            for index in exported_map["Cell"].keys():
                cell = exported_map["Cell"][index]
                difficulty = cell[2]
                explored = cell[3]
                priority = cell[4]
                utility = cell[5]
                if difficulty == "inf":
                    difficulty = math.inf
                if priority == "-inf":
                    priority = -math.inf
                if utility == "-inf":
                    utility = -math.inf
                if explored == -1:
                    self.nobstacle += 1
                if self.dump_datas and utility == 1:
                    self.total_difficulty += difficulty
                a = Cell(self.agent_counter, self, index, difficulty, explored,
                         priority, utility)
                self.grid.place_agent(a, index)
                self.agent_counter += 1

            for index in exported_map["Injured"].keys():
                a = Injured(self.agent_counter, self, index)
                self.schedule.add(a)
                self.grid.place_agent(a, index)
                self.agent_counter += 1

        # create robotic agents
        row = 0
        starting_coord = []
        # data collection number of beans requested
        if self.dump_datas:
            self.deployed_beans_at_start = 0
        # generating the list for the starting position of robots
        for c in range(self.grid.width):
            # take the agent cell
            cell = [
                e for e in self.grid.get_cell_list_contents(tuple([c, row]))
                if isinstance(e, Cell)
            ][0]
            if cell.explored != -1:
                starting_coord.append(c)
        for i in range(0, self.nrobots):
            column = rnd.choice(starting_coord)
            a = Robot(self.agent_counter, self, tuple([column, row]),
                      self.radar_radius)
            self.schedule.add(a)
            self.grid.place_agent(a, (column, row))
            self.agent_counter += 1
            # create initial frontier: add cell in front of the robot if valid and not obstacles
            cell = [
                e for e in self.grid.get_cell_list_contents(
                    tuple([column, row + 1])) if isinstance(e, Cell)
            ][0]
            if cell.explored == 0:
                self.frontier.add(tuple([column, row + 1]))
            try:
                cell = [
                    e for e in self.grid.get_cell_list_contents(
                        tuple([column + 1, row + 1])) if isinstance(e, Cell)
                ][0]
                if cell.explored == 0:
                    self.frontier.add(tuple([column + 1, row + 1]))
            except:
                pass
            try:
                cell = [
                    e for e in self.grid.get_cell_list_contents(
                        tuple([column - 1, row + 1])) if isinstance(e, Cell)
                ][0]
                if cell.explored == 0:
                    self.frontier.add(tuple([column - 1, row + 1]))
            except:
                pass

            cell = [
                e
                for e in self.grid.get_cell_list_contents(tuple([column, row]))
                if isinstance(e, Cell)
            ][0]
            # in the cell where some robots are deployed, only one bean is deployed
            if not cell.wifi_bean:
                cell.wifi_bean = True
                for index in self.grid.get_neighborhood(
                        cell.pos,
                        "moore",
                        include_center=False,
                        radius=self.wifi_range):
                    cell = [
                        e for e in self.grid.get_cell_list_contents(index)
                        if isinstance(e, Cell)
                    ][0]
                    cell.wifi_covered = True
                if self.dump_datas:
                    self.deployed_beans_at_start += 1

    # what the model does at each time step
    def step(self):

        # data collection for alpha variation
        if self.alpha_variation:
            sim_step = self.get_step(self)
            self.alpha_step[sim_step] = list()

        # call step function for all of the robots in random order
        self.schedule.step()

        if self.dump_datas:
            self.dc_robot_status.collect(self)
        if self.optimization_task:
            self.total_idling_time += self.get_number_robots_status(
                self, "idling")
        if self.gamma_variation:
            distances = self.compute_robot_distances(self)
            self.gamma_df = self.gamma_df.append(
                {
                    "step": self.get_step(self),
                    "mean": distances[0],
                    "std": distances[1]
                },
                ignore_index=True,
                sort=False)
        # if all seen cells have benn explored, stop the simulation
        # we do this so if there are unreachable cells, the cannot be seen, so the simulation stops anyway
        stop_exploration_done = True
        for node in self.seen_graph.nodes():
            cell = [
                obj for obj in self.grid.get_cell_list_contents(node)
                if isinstance(obj, Cell)
            ][0]
            if cell.explored == 0 or cell.explored == 1:
                stop_exploration_done = False
        stop_no_robots = False
        if len([x for x in self.schedule.agents if isinstance(x, Robot)]) == 0:
            stop_no_robots = True
        stop = stop_exploration_done or stop_no_robots

        if stop:
            print("Simultation ended")
            # Data collection
            if self.dump_datas:
                df = pd.read_csv(self.time_csv)
                df = df.append(
                    {
                        "nrobots": self.nrobots,
                        "ncells": self.ncells,
                        "steps": self.schedule.steps,
                        "total_difficulty": self.total_difficulty,
                        "beans_deployed": self.get_number_bean_deployed(self)
                    },
                    ignore_index=True)
                df.to_csv(self.time_csv, index=False)

                df_robots_status = self.dc_robot_status.get_model_vars_dataframe(
                )
                df = pd.read_csv(self.robot_status_csv)
                if len(df["sim_id"]) == 0:
                    df_robots_status["sim_id"] = 0
                else:
                    df_robots_status["sim_id"] = df["sim_id"][df.index[-1]] + 1
                df = df.append(df_robots_status, ignore_index=True, sort=False)
                df.to_csv(self.robot_status_csv, index=False)

            if self.alpha_variation:
                mean = round(np.mean(self.costs_each_path), 3)
                std = round(np.std(self.costs_each_path), 3)
                df = pd.read_csv(self.alpha_csv)
                df = df.append(
                    {
                        "nrobots": self.nrobots,
                        "radar_radius": self.radar_radius,
                        "alpha": self.alpha,
                        "gamma": self.gamma,
                        "mean": mean,
                        "std": std
                    },
                    ignore_index=True)
                df.to_csv(self.alpha_csv, index=False)

                tmp_df = pd.DataFrame(columns=["step", "cost"])
                for s, costs in zip(self.alpha_step.keys(),
                                    self.alpha_step.values()):
                    if not costs:
                        tmp_df = tmp_df.append({
                            "step": s,
                            "cost": -1
                        },
                                               ignore_index=True,
                                               sort=False)
                        continue
                    for c in costs:
                        tmp_df = tmp_df.append({
                            "step": s,
                            "cost": c
                        },
                                               ignore_index=True,
                                               sort=False)
                df = pd.read_csv(self.alpha_step_csv)
                if len(df["sim_id"]) == 0:
                    tmp_df["sim_id"] = 0
                else:
                    tmp_df["sim_id"] = df["sim_id"][df.index[-1]] + 1
                tmp_df["nrobots"] = self.nrobots
                tmp_df["radar_radius"] = self.radar_radius
                tmp_df["alpha"] = self.alpha
                tmp_df["gamma"] = self.gamma
                df = df.append(tmp_df, ignore_index=True, sort=False)
                df.to_csv(self.alpha_step_csv, index=False)

            if self.gamma_variation:
                df = pd.read_csv(self.gamma_csv)
                if len(df["sim_id"]) == 0:
                    self.gamma_df["sim_id"] = 0
                else:
                    self.gamma_df["sim_id"] = df["sim_id"][df.index[-1]] + 1
                self.gamma_df["nrobots"] = self.nrobots
                self.gamma_df["radar_radius"] = self.radar_radius
                self.gamma_df["alpha"] = self.alpha
                self.gamma_df["gamma"] = self.gamma
                df = df.append(self.gamma_df, ignore_index=True, sort=False)
                df.to_csv(self.gamma_csv, index=False)

            self.running = False

    def run_model(self):
        while (True):
            # search for unexplored cells
            stop = True
            for node in self.seen_graph.nodes():
                cell = [
                    obj for obj in self.grid.get_cell_list_contents(node)
                    if isinstance(obj, Cell)
                ][0]
                if cell.explored == 0 or cell.explored == 1:
                    stop = False
            # if all seen cells have benn explored, stop the simulation
            # we do this so if there are unreachable cells, the cannot be seen, so the simulation stops anyway
            if stop:
                self.running = False
                break
            else:
                self.step()

    # Data collection utilities
    @staticmethod
    def get_step(m):
        return m.schedule.steps

    # these two should go faster since cells are not in the scheduler anymore
    @staticmethod
    def get_number_robots_status(m, status):
        status_value = {
            "idling": 0,
            "travelling": 1,
            "exploring": 2,
            "deploying_bean": 3
        }
        return len([
            x for x in m.schedule.agents
            if isinstance(x, Robot) and x.status == status_value[status]
        ])

    @staticmethod
    def get_number_bean_deployed(m):
        return sum([
            x.number_bean_deployed
            for x in m.schedule.agents if isinstance(x, Robot)
        ]) + m.deployed_beans_at_start

    # function for gamma variation
    @staticmethod
    def compute_robot_distances(m):
        nrobots = m.nrobots
        T_up = np.full(
            (nrobots, nrobots),
            0.0)  # if it's only zero, numpy represents only integers
        # didn't dig deep in numpy doc but it looks like it handles triangualr matrices as "normal" matrices, so
        # i just initilize a full matrix and then i'll use it as a triangular.
        robots = [x for x in m.schedule.agents if isinstance(x, Robot)]
        # the order of the robots in robots can change from step to step (due to the random scheduler),
        # This shouldn't create any type of problem, but to avoid a lot of problems with indexes later on
        # we sort them basing on the unique_id
        robots.sort(key=lambda x: x.unique_id)
        # I need the lowest id to shift back the ids to fit the matrices coordinations
        lowest_id = robots[0].unique_id
        for r in robots:
            matrix_id_row = r.unique_id - lowest_id
            # the distance of a robot to itself is zero by definition
            y1, x1 = r.pos
            for i in range(matrix_id_row + 1, nrobots):
                r2 = robots[i]  # i can do this because they are sorted
                y2, x2 = r2.pos
                T_up[matrix_id_row][i] = distance.euclidean([x1, y1], [x2, y2])
        mean_dist_robots = list()
        # the robot 0 has only the rows
        mean_robot_zero = sum(T_up[0, 1:nrobots])
        mean_dist_robots.append(mean_robot_zero)
        for i in range(1, nrobots -
                       1):  # the last row has no values, i iters the rows
            mean_robot = (sum(T_up[0:i, i]) +
                          sum(T_up[i, i + 1:nrobots])) / (nrobots - 1)
            mean_dist_robots.append(mean_robot)
        # last robot has only the columns
        mean_last_robot = sum(T_up[0:nrobots - 1, nrobots - 1])
        mean_dist_robots.append(mean_last_robot)
        return tuple([
            round(np.mean(mean_dist_robots), 3),
            round(np.std(mean_dist_robots), 3)
        ])
Ejemplo n.º 5
0
class World(Model):
    """
    Model for the electricity landscape world
    """

    def __init__(
        self,
        initialization_year,
        scenario_file=None,
        fitting_params=None,
        long_term_fitting_params=None,
        future_price_uncertainty_m=None,
        future_price_uncertainty_c=None,
        carbon_price_scenario=None,
        demand_change=None,
        demand_distribution=None,
        number_of_steps=8,
        total_demand=None,
        number_of_agents=None,
        market_time_splices=8,
        data_folder="ElecSim_Output",
        time_run=False,
        nuclear_subsidy=None,
        highest_demand=None,
        log_level="warning",
        client_rl=None,
        distribution_name=None,
        dropbox=None,
        gencos_rl=[],
        write_data_to_file=True,
        rl_port_number=9920,
    ):
        """
        Initialize an electricity market in a particular country. Provides the ability to change scenarios from this constructor.
        :param int initialization_year: Year to begin simulation.
        :param list: float carbon_price_scenario: Scenario containing carbon price for each year of simulation.
        :param list: float demand_change: Scenario containing change in demand between each year of simulation.
        :param int number_of_steps: Total number of years to run scenario.
        :param int total_demand: Total size of country's demand.
        :param str data_folder: Directory and folder to save run data to
        :param bool time_run:
        :param str log_level:
        """

        self.start = perf_counter()
        logger.info("start: {}".format(self.start))
        # Set up model objects
        self.year_number = initialization_year
        self.years_from_start = 0
        self.step_number = 0
        self.unique_id_generator = 0
        self.time_run = time_run
        self.max_number_of_steps = number_of_steps
        self.average_electricity_price = 0
        self.market_time_splices = market_time_splices
        self.nuclear_subsidy = nuclear_subsidy
        self.dropbox = dropbox
        self.gencos_rl = gencos_rl
        self.write_data_to_file = write_data_to_file

        self.set_log_level(log_level)

        self.scenario_file = scenario_file
        self.overwrite_scenario_file(scenario_file)

        self.override_highest_demand(highest_demand)
        self.override_carbon_scenario(carbon_price_scenario)
        self.override_demand_change(demand_change)

        self.demand_distribution_uncertainty = demand_distribution
        self.distribution_name = distribution_name

        self.override_total_demand(total_demand, number_of_agents)

        self.schedule = OrderedActivation(self)

        # Import company data including financials and plant data
        plant_data = elecsim.scenario.scenario_data.power_plants
        financial_data = elecsim.scenario.scenario_data.company_financials

        if self.gencos_rl:
            self.bidding_client = PolicyClient(
                "http://127.0.0.1:{}".format(rl_port_number)
            )

        # Initialize generation companies using financial and plant data
        self.initialize_gencos(financial_data, plant_data, gencos_rl)

        self.last_added_plant = None
        self.last_added_plant_bids = None

        # Create PowerExchange

        if self.market_time_splices == 1:
            self.PowerExchange = PowerExchange(self, demand_distribution)
            self.demand = Demand(
                self,
                self.unique_id_generator,
                elecsim.scenario.scenario_data.segment_time,
                elecsim.scenario.scenario_data.segment_demand_diff,
            )
        elif self.market_time_splices > 1:
            self.PowerExchange = PowerExchange(self, demand_distribution)
            # self.PowerExchange = HighTemporalExchange(self)
            self.demand = MultiDayDemand(
                self,
                self.unique_id_generator,
                elecsim.scenario.scenario_data.multi_year_data,
            )
        else:
            raise ValueError("market_time_splices must be equal to or larger than 1.")

        self.running = True
        self.beginning_of_year = False

        self.continue_investing = 0
        self.over_invested = False

        self.unique_id_generator += 1
        self.schedule.add(self.demand)
        self.create_data_loggers(data_folder)

        self.client = client_rl
        if (
            elecsim.scenario.scenario_data.investment_mechanism == "RL"
            and self.client is not None
            and self.gencos_rl
        ):
            # self.client = PolicyClient("http://rllibserver:9900")

            self.eid = self.client.start_episode(training_enabled=True)
            self.intial_obs = LatestMarketData(self).get_RL_investment_observations()
            # logger.info("self.intial_obs: {}".format(self.intial_obs))
        elif elecsim.scenario.scenario_data.investment_mechanism == "future_price_fit":
            self.future_price_uncertainty_m = future_price_uncertainty_m
            self.future_price_uncertainty_c = future_price_uncertainty_c
            if fitting_params is not None:
                self.fitting_params = fitting_params
            elif long_term_fitting_params is not None:
                self.long_term_fitting_params = long_term_fitting_params
                self.fitting_params = None
            else:
                raise ValueError(
                    "If using future_price_fit you must enter a value for long_term_fitting_params or fitting_params in the constructor of World"
                )

    def step(self, carbon_price=None):
        """Advance model by one step"""
        self.beginning_of_year = False

        if self.step_number % self.market_time_splices == 0:
            self.start = time.perf_counter()
            self.operate_constructed_plants()
            if self.step_number != 0:
                self.year_number += 1
                self.years_from_start += 1
                # self.operate_constructed_plants()
                self.beginning_of_year = True
                # logger.info("year: {}".format(self.year_number))
                print("{}:".format(self.year_number), end="", flush=True)
            else:
                print("{}:".format(self.year_number), end="", flush=True)

        obs = self.schedule.step()
        self.operate_constructed_plants()

        if self.over_invested:
            return self.datacollector.get_model_vars_dataframe(), self.over_invested

        self.continue_investing = 0
        if carbon_price is not None:
            elecsim.scenario.scenario_data.carbon_price_scenario[
                self.year_number + 1
            ] = carbon_price
        else:
            elecsim.scenario.scenario_data.carbon_price_scenario = (
                elecsim.scenario.scenario_data.carbon_price_scenario
            )

        if self.beginning_of_year:
            self.dismantle_old_plants()
            self.dismantle_unprofitable_plants()

        self.average_electricity_price = self.PowerExchange.tender_bids(
            self.demand.segment_hours, self.demand.segment_consumption
        ).accepted_price.mean()
        self.PowerExchange.price_duration_curve = []

        carbon_emitted = self.get_carbon_emitted(self)
        self.settle_gencos_financials()

        self.datacollector.collect(self)
        self.delete_old_bids()

        self.step_number += 1
        print(".", end="", flush=True)

        if self.write_data_to_file:
            self.write_scenario_data()

        if isinstance(self.average_electricity_price, np.ndarray):
            self.average_electricity_price = self.average_electricity_price[0]

        if self.step_number % self.market_time_splices == 0:
            end = time.perf_counter()
            print("time taken: {}".format(end - self.start))
            # get_capacity_factor.cache_clear()

        if (
            self.step_number == self.max_number_of_steps
            and elecsim.scenario.scenario_data.investment_mechanism == "RL"
            and self.gencos_rl
        ):
            obs = LatestMarketData(self).get_RL_investment_observations()
            self.client.end_episode(self.eid, observation=obs)
            del self.client

        logger.debug(self.datacollector.get_model_vars_dataframe())
        return abs(self.average_electricity_price), abs(carbon_emitted)
        # return self.datacollector.get_model_vars_dataframe(), self.over_invested

    def initialize_gencos(self, financial_data, plant_data, gencos_rl):
        """
        Creates generation company agents based on financial data and power plants owned. Estimates cost parameters
         of each power plant if data not for power plant not available.
        :param financial_data: Data containing information about generation company's financial status
        :param plant_data: Data containing information about generation company's plants owned, start year and name.
        """

        financial_data = pd.merge(financial_data, plant_data, on="Company", how="inner")
        financial_data = financial_data[["Company", "cash_in_bank"]]

        # Initialising generator company data
        financial_data.cash_in_bank = financial_data.cash_in_bank.replace("nan", np.nan)
        financial_data.cash_in_bank = financial_data.cash_in_bank.fillna(0)
        companies_groups = plant_data.groupby("Company")
        company_financials = financial_data.groupby("Company")

        logger.info("Initialising generation companies with their power plants.")

        # Initialize generation companies with their respective power plants
        for gen_id, ((name, data), (_, financials)) in enumerate(
            zip(companies_groups, company_financials), 0
        ):
            rl_bidding = False
            if financials.Company.iloc[0] != name:
                raise ValueError(
                    "Company financials name ({}) and agent name ({}) do not match.".format(
                        financials.Company.iloc[0], name
                    )
                )
            elif financials.Company.iloc[0] in gencos_rl:
                rl_bidding = True

            gen_co = GenCo(
                unique_id=gen_id,
                model=self,
                difference_in_discount_rate=round(uniform(-0.03, 0.03), 3),
                look_back_period=randint(3, 7),
                name=name,
                money=financials.cash_in_bank.iloc[0],
                rl_bidding=rl_bidding,
            )
            self.unique_id_generator += 1
            # Add power plants to generation company portfolio
            # parent_directory = os.path.dirname(os.getcwd())
            pickle_directory = (
                "{}/../elecsim/data/processed/pickled_data/power_plants/".format(
                    ROOT_DIR
                )
            )

            for plant in data.itertuples():
                try:
                    power_plant = pickle.load(
                        open(
                            "{}{}-{}.pickle".format(
                                pickle_directory, plant.Name, plant.Start_date
                            ),
                            "rb",
                        )
                    )
                except (OSError, IOError, FileNotFoundError, EOFError) as e:
                    logger.info("plant: {}".format(plant))
                    power_plant = create_power_plant(
                        plant.Name,
                        plant.Start_date,
                        plant.Simplified_Type,
                        plant.Capacity,
                    )
                    pickle.dump(
                        power_plant,
                        open(
                            "{}{}-{}.pickle".format(
                                pickle_directory, plant.Name, plant.Start_date
                            ),
                            "wb",
                        ),
                    )
                gen_co.plants.append(power_plant)
            logger.debug("Adding generation company: {}".format(gen_co.name))
            self.schedule.add(gen_co)
        logger.info("Added generation companies.")

    def get_running_plants(self, plants):
        for plant in plants:
            # if plant.name in elecsim.scenario.scenario_data.known_plant_retirements:

            if (
                plant.construction_year <= 1990
                and plant.name != "invested_plant"
                and plant.name
                not in elecsim.scenario.scenario_data.known_plant_retirements
            ):
                # Reset old plants that have been modernised with new construction year
                plant.construction_year = randint(
                    self.year_number - 15, self.year_number
                )
                # yield plant
            elif plant.name in elecsim.scenario.scenario_data.known_plant_retirements:
                plant.construction_year = (
                    elecsim.scenario.scenario_data.known_plant_retirements[plant.name]
                    - (
                        plant.operating_period
                        + plant.construction_period
                        + plant.pre_dev_period
                    )
                    - 1
                )
                logger.info(
                    "plant.name: {}, plant.construction_year: {}".format(
                        plant.name, plant.construction_year
                    )
                )

            if (
                plant.construction_year
                + plant.operating_period
                + plant.construction_period
                + plant.pre_dev_period
                >= self.year_number
            ):
                yield plant
            else:
                logger.debug(
                    "Taking the plant '{}' out of service, year of construction: {}".format(
                        plant.name, plant.construction_year
                    )
                )

    def dismantle_old_plants(self):
        """
        Remove plants that are past their lifetime agent from each agent from their plant list
        """

        gencos = self.get_gencos()

        for genco in gencos:
            plants_filtered = list(self.get_running_plants(genco.plants))
            genco.plants = plants_filtered

    def dismantle_unprofitable_plants(self):

        gencos = self.get_gencos()

        for genco in gencos:
            profitable_plants = list(self.filter_plants_with_no_income(genco.plants))
            genco.plants = profitable_plants

    def filter_plants_with_no_income(self, plants):
        for plant in plants:
            if (self.year_number > 7) and (
                plant.get_year_of_operation() + 7 < self.year_number
            ):
                historic_bids = plant.historical_bids
                # logger.info("historic_bids {}".format(historic_bids))
                # years_to_look_into = list(range(self.year_number,self.year_number-7,-1))

                seven_years_previous = self.year_number - 7
                if historic_bids:
                    if historic_bids[-1].year_of_bid > seven_years_previous:
                        yield plant
                    else:
                        logger.debug(
                            "Plant {}, type {} is unprofitable. Last accepted bid: {}".format(
                                plant.name,
                                plant.plant_type,
                                historic_bids[-1].year_of_bid,
                            )
                        )
                        for bid in plant.accepted_bids:
                            del bid
                else:
                    logger.debug(
                        "Plant {}, type {} is unprofitable.".format(
                            plant.name, plant.plant_type
                        )
                    )
                    for bid in plant.accepted_bids:
                        del bid
                # bids_to_check = list(filter(lambda x: x.year_of_bid in years_to_look_into, historic_bids))
                # total_income_in_previous_years = sum(bid.price_per_mwh for bid in bids_to_check)
                # for bids in reversed(historic_bids):
                #     logger.info("bids.year_of_bid: {}".format(bids.year_of_bid))

                # if total_income_in_previous_years > 0:
                #     yield plant
                # else:
                #     logger.debug("Taking plant: {} out of service.".format(plant.name))
            else:
                yield plant

    def get_profitable_plants(self, plants):
        for plant in plants:
            if (
                self.step_number > 7
                and plant.get_year_of_operation() + 7 > self.year_number
            ):
                historic_bids = plant.historical_bids
                pass

    def operate_constructed_plants(self, minimum_operation_year=2018):
        gencos = self.get_gencos()
        logger.debug("gencos: {}".format(gencos))
        for genco in gencos:
            logger.debug("genco plants: {}".format(genco.plants))
            for plant in genco.plants:
                # logger.debug("plant: {}, year_number: {}, construction year+constructioon_period+predev: {}".format(plant, self.year_number, plant.construction_year + plant.construction_period + plant.pre_dev_period))
                if plant.construction_year <= minimum_operation_year:
                    plant.is_operating = True
                elif (plant.is_operating is False) and (
                    self.year_number
                    >= plant.construction_year
                    + plant.construction_period
                    + plant.pre_dev_period
                ):
                    plant.is_operating = True

    def overwrite_scenario_file(self, scenario_file):
        if scenario_file:
            split_directory = scenario_file.split("/")
            file_name = split_directory[-1]
            spec = importlib.util.spec_from_file_location(file_name, scenario_file)
            scenario_import = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(scenario_import)
            scen_mod.overwrite_scenario_file(scenario_import)

    def settle_gencos_financials(self):
        gencos = self.get_gencos()
        for genco in gencos:
            genco.settle_accounts()
            # genco.delete_old_bids()

    def delete_old_bids(self):
        gencos = self.get_gencos()
        for genco in gencos:
            genco.delete_old_bids()

    def get_gencos(self):
        gencos = [genco for genco in self.schedule.agents if isinstance(genco, GenCo)]
        return gencos

    def clear_all_bids(self):
        gencos = self.get_gencos()
        for genco in gencos:
            genco.delete_old_bids()

    @staticmethod
    def get_capacity_of_plants(model, plant_type):
        gencos = model.get_gencos()
        plants = [
            plant
            for genco in gencos
            for plant in genco.plants
            if plant.plant_type == plant_type and plant.is_operating
        ]
        total_capacity = sum(plant.capacity_mw for plant in plants)

        return total_capacity

    @staticmethod
    def get_all_plants(model):
        gencos = model.get_gencos()
        plants = [plant for genco in gencos for plant in genco.plants]
        return plants

    @staticmethod
    def get_current_carbon_tax(model):
        carbon_tax = elecsim.scenario.scenario_data.carbon_price_scenario[
            model.years_from_start
        ]
        return carbon_tax

    @staticmethod
    def get_genco_wealth(model):
        gencos = model.get_gencos()
        total_wealth = 0
        for genco in gencos:
            total_wealth += genco.money
        return total_wealth

    @staticmethod
    def get_electricity_cost(model):
        return model.average_electricity_price

    @staticmethod
    def get_carbon_emitted(model):
        gencos = model.get_gencos()
        bids = World.get_accepted_bids(gencos, FuelPlant)

        carbon_emitted = sum(
            bid.capacity_bid * bid.plant.fuel.co2_density
            for bid in bids
            if isinstance(bid.plant, FuelPlant)
        )
        return carbon_emitted

    @staticmethod
    def get_accepted_bid_capacity(model, plant_type):
        gencos = model.get_gencos()
        plants = [
            plant
            for genco in gencos
            for plant in genco.plants
            if plant.plant_type == plant_type and plant.is_operating
        ]
        capacity_contributed = sum(
            bid.capacity_bid for plant in plants for bid in plant.accepted_bids
        )
        return capacity_contributed

    @staticmethod
    def get_accepted_bid_capacity_per_segment_hour(model):
        gencos = model.get_gencos()
        plants = [
            plant for genco in gencos for plant in genco.plants if plant.is_operating
        ]
        # capacity_contributed = [ if bid.segment_hours==]
        bids_dataframe = [
            bid.to_dict() for plant in plants for bid in plant.accepted_bids
        ]
        return bids_dataframe

    @staticmethod
    def get_accepted_bids(gencos, plant_type=None):
        if plant_type:
            bids = list(
                accepted_bids
                for genco in gencos
                for plants in genco.plants
                for accepted_bids in plants.accepted_bids
                if isinstance(plants, plant_type)
            )
        else:
            bids = list(
                accepted_bids
                for genco in gencos
                for plants in genco.plants
                for accepted_bids in plants.accepted_bids
            )

        return bids

    def stratify_data(self, demand):
        power_plants = elecsim.scenario.scenario_data.power_plants
        frac_to_scale = demand / power_plants.Capacity.sum()

        stratified_sample = power_plants.groupby(["Fuel"], as_index=False).apply(
            lambda x: x.sample(frac=frac_to_scale, replace=True)
        )
        return stratified_sample

    def set_log_level(self, log_level):
        if log_level.lower() == "warning":
            logging.basicConfig(level=logging.WARNING)
        elif log_level.lower() == "info":
            logging.basicConfig(level=logging.INFO)
        elif log_level.lower() == "debug":
            logging.basicConfig(level=logging.DEBUG)
        else:
            raise ValueError(
                "log_level must be warning, info or debug and not {}".format(log_level)
            )

    def create_data_loggers(self, data_folder):
        self.data_folder = data_folder
        self.datacollector = DataCollector(
            model_reporters={
                "contributed_CCGT": lambda m: self.get_accepted_bid_capacity(m, "CCGT"),
                "contributed_Coal": lambda m: self.get_accepted_bid_capacity(m, "Coal"),
                "contributed_Onshore": lambda m: self.get_accepted_bid_capacity(
                    m, "Onshore"
                ),
                "contributed_Offshore": lambda m: self.get_accepted_bid_capacity(
                    m, "Offshore"
                ),
                "contributed_PV": lambda m: self.get_accepted_bid_capacity(m, "PV"),
                "contributed_Nuclear": lambda m: self.get_accepted_bid_capacity(
                    m, "Nuclear"
                ),
                "contributed_Recip_gas": lambda m: self.get_accepted_bid_capacity(
                    m, "Recip_gas"
                ),
                "contributed_Biomass": lambda m: self.get_accepted_bid_capacity(
                    m, "Biomass"
                ),
                # "hourly_accepted_bids": lambda m: self.get_accepted_bid_capacity_per_segment_hour(m),
                "total_CCGT": lambda m: self.get_capacity_of_plants(m, "CCGT"),
                "total_Coal": lambda m: self.get_capacity_of_plants(m, "Coal"),
                "total_Onshore": lambda m: self.get_capacity_of_plants(m, "Onshore"),
                "total_Offshore": lambda m: self.get_capacity_of_plants(m, "Offshore"),
                "total_PV": lambda m: self.get_capacity_of_plants(m, "PV"),
                "total_Nuclear": lambda m: self.get_capacity_of_plants(m, "Nuclear"),
                "total_Recip_gas": lambda m: self.get_capacity_of_plants(
                    m, "Recip_gas"
                ),
                "Carbon_tax": lambda m: self.get_current_carbon_tax(m),
                "total_genco_wealth": lambda m: self.get_genco_wealth(m),
                "Electricity_cost": lambda m: self.get_electricity_cost(m),
                "Carbon_emitted": lambda m: self.get_carbon_emitted(m),
            }
        )

    def override_total_demand(self, total_demand, number_of_agents=None):
        self.total_demand = total_demand
        if total_demand is not None:
            elecsim.scenario.scenario_data.power_plants = self.stratify_data(
                total_demand
            )
            demand_modifier = (
                elecsim.scenario.scenario_data.power_plants.Capacity.sum()
                / elecsim.scenario.scenario_data.segment_demand_diff[-1]
            ) / 1.6
            logger.info("demand_modifier: {}".format(demand_modifier))
            logger.info(
                "total available capacity: {}".format(
                    elecsim.scenario.scenario_data.power_plants.Capacity.sum()
                )
            )
            elecsim.scenario.scenario_data.segment_demand_diff = [
                demand_modifier * demand
                for demand in elecsim.scenario.scenario_data.segment_demand_diff
            ]

            if number_of_agents is not None:
                total_plants = len(elecsim.scenario.scenario_data.power_plants)
                fraction_to_replace = total_plants / number_of_agents
                company_names = sample(
                    list(elecsim.scenario.scenario_data.power_plants.Company.unique()),
                    number_of_agents,
                )

                company_name_repeated = np.repeat(
                    company_names, int(fraction_to_replace)
                )
                company_name_repeated = np.append(
                    company_name_repeated,
                    np.array(
                        ["company_{}".format(number_of_agents - 1) for i in range(100)]
                    ),
                )

                elecsim.scenario.scenario_data.power_plants.Company = (
                    company_name_repeated[:total_plants]
                )

    def override_highest_demand(self, highest_demand):
        if highest_demand:
            elecsim.scenario.scenario_data.initial_max_demand_size = highest_demand

    def override_demand_change(self, demand_change):
        if demand_change:
            elecsim.scenario.scenario_data.yearly_demand_change = demand_change[1:]
            self.demand_change_name = str(demand_change[0]).replace(".", "")
        else:
            self.demand_change_name = "none"

    def override_carbon_scenario(self, carbon_price_scenario):
        if carbon_price_scenario:
            elecsim.scenario.scenario_data.carbon_price_scenario = (
                carbon_price_scenario[1:]
            )
            self.carbon_scenario_name = str(carbon_price_scenario[0]).replace(".", "")
        else:
            self.carbon_scenario_name = "none"

    def write_scenario_data(self):
        if self.step_number == self.max_number_of_steps:
            parent_directory = os.path.dirname(os.getcwd())

            directory = "{}/{}/".format(parent_directory, self.data_folder)
            if not os.path.exists(directory):
                os.makedirs(directory)

            filename = "demand_{}-carbon_{}-datetime_{}-capacity_{}-demand_distribution_{}.csv".format(
                self.demand_change_name,
                self.carbon_scenario_name,
                dt.datetime.now().strftime("%Y-%m-%d_%H-%M-%S"),
                1,
                self.distribution_name,
                # self.scenario_file.split("/")[-1].split(".")[0],
            )

            directory_filename = "{}/{}.csv".format(directory, filename)

            results_df = self.datacollector.get_model_vars_dataframe()
            results_df.to_csv(directory_filename)

            class TransferData:
                def __init__(self, access_token):
                    self.access_token = access_token

                def upload_file(self, file_from, file_to):
                    """upload a file to Dropbox using API v2"""
                    dbx = dropbox.Dropbox(self.access_token)

                    with open(file_from, "rb") as f:
                        dbx.files_upload(f.read(), file_to)

            if self.dropbox:
                transferData = TransferData(access_token)

                file_from = "/{}".format(directory_filename)
                file_to = "/{}".format(filename)

                # API v2
                transferData.upload_file(file_from, file_to)

        if self.step_number == self.max_number_of_steps:
            end = perf_counter()
            time_elapased = end - self.start

            self.write_timing_results(end, time_elapased)

    def write_timing_results(self, end, time_elapased):
        if self.time_run:
            timings_data = pd.DataFrame(
                {
                    "time": [time_elapased],
                    "carbon": [elecsim.scenario.scenario_data.carbon_price_scenario[0]],
                    "installed_capacity": [
                        elecsim.scenario.scenario_data.power_plants.Capacity.sum()
                    ],
                    "datetime": [dt.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")],
                }
            )

            parent_directory = os.path.dirname(os.getcwd())

            with open("{}/{}/timing.csv".format(ROOT_DIR, self.data_folder), "a") as f:
                timings_data.to_csv(f, header=False)
            logger.info("end: {}".format(end))
            logger.info(
                "time_elapsed: {}, carbon: {}, size: {}".format(
                    time_elapased,
                    elecsim.scenario.scenario_data.carbon_price_scenario[0],
                    elecsim.scenario.scenario_data.power_plants.Capacity.sum(),
                )
            )
Ejemplo n.º 6
0
class VirusModel(Model):
    """A virus model with some number of agents"""
    def __init__(self):
        # self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=prob)
        # self.G = nx.erdos_renyi_graph(n=3, p=0.5)
        self.G = nx.Graph()
        self.G.add_node(0)
        self.G.add_node(1)
        self.G.add_node(2)
        self.G.add_node(3)
        self.G.add_node(4)
        self.G.add_node(4)
        self.G.add_edge(0, 1)
        self.G.add_edge(0, 2)
        self.G.add_edge(0, 3)
        self.G.add_edge(0, 4)
        self.G.add_edge(0, 5)
        self.G.add_edge(1, 4)
        self.G.add_edge(4, 5)
        self.grid = NetworkGrid(self.G)

        self.rooms = {}
        self.rooms[0] = {"name": "Wejście", "rates": {}}
        self.rooms[1] = {"name": "Czytelnia", "rates": {"Nauka": 2}}
        self.rooms[2] = {"name": "Chillout", "rates": {"Relaks": 10}}
        self.rooms[3] = {"name": "Biuro", "rates": {"Praca": 1.5}}
        self.rooms[4] = {"name": "Toaleta", "rates": {"Toaleta": 30}}
        self.rooms[5] = {
            "name": "Kawiarnia",
            "rates": {
                "Jedzenie": 12,
                "Kultura": 0.5
            }
        }

        collector_dict = {}
        for i, room in enumerate(self.rooms):
            collector_dict[self.rooms[i]["name"]] = lambda model, i=i: len(
                model.grid.get_cell_list_contents([i])) - 1
        self.datacollector = DataCollector(collector_dict)

        self.schedule = RandomActivation(self)

        # Create agents
        for i, node in enumerate(self.G.nodes()):
            r = RoomAgent(i, self, self.rooms[i]["name"],
                          self.rooms[i]["rates"])
            self.schedule.add(r)

            # Add the agent to the node
            self.grid.place_agent(r, node)

        self.prob_needs = {
            "Jedzenie": [4, 0.6],
            "Toaleta": [2, 0.6],
            "Relaks": [5, 1]
        }
        self.prob_studs = {
            "Nauka": [2, 1.5],
            "Praca": [0, 0.5],
            "Kultura": [0, 1.0]
        }
        self.prob_works = {
            "Nauka": [0, 0.3],
            "Praca": [6, 1.0],
            "Kultura": [0, 0.2]
        }
        self.prob_tours = {
            "Nauka": [0, 0.3],
            "Praca": [0, 0.5],
            "Kultura": [1, 1.0]
        }
        self.prob_local = {
            "Nauka": [1, 0.7],
            "Praca": [2, 0.9],
            "Kultura": [1, 1.0]
        }

        # godziny        0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
        self.rate_studs = [
            0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
            0, 0
        ]
        self.rate_works = [
            0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
            0, 0
        ]
        self.rate_tours = [
            0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 3, 3, 3, 4, 4, 4, 6, 6, 4, 3, 2,
            0, 0
        ]
        self.rate_local = [
            0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 4, 4, 2, 2, 4, 5, 6, 6, 6, 3, 0,
            0, 0
        ]

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

        self.tm = 0 * 60
        self.count = 0

    def get_sample(self, probs):
        ret = {}
        for k, [m, s] in probs.items():
            tm = int(np.clip(np.random.normal(m, s) * 60, 15, 600))
            ret[k] = tm
        return ret

    def step(self):
        # prepare list for the satisfied agents
        self.satisfied = []

        # add new agents
        hour = int(self.tm / 60)
        if (hour > 23):
            hour = 0

        for i in range(self.rate_studs[hour]):
            a = HumanAgent(100 + self.count, self,
                           self.get_sample(self.prob_needs),
                           self.get_sample(self.prob_studs))
            self.schedule.add(a)
            self.grid.place_agent(a, 0)
            self.count += 1

        for i in range(self.rate_works[hour]):
            a = HumanAgent(100 + self.count, self,
                           self.get_sample(self.prob_needs),
                           self.get_sample(self.prob_works))
            self.schedule.add(a)
            self.grid.place_agent(a, 0)
            self.count += 1

        # update system
        self.schedule.step()

        # collect data
        self.datacollector.collect(self)

        # make time step
        self.tm = self.tm + 1
        if (self.tm > 24 * 60):
            self.datacollector.get_model_vars_dataframe().to_csv("one_day.csv")
            self.tm = 0

        # remove satisfied agents from the system
        for a in self.satisfied:
            print(a.unique_id, a.goals, "is satisfied")
            self.grid.move_agent(a, 0)
            self.grid._remove_agent(a, 0)
            self.schedule.remove(a)

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

    def find_best_room(self, goal):
        #print("Looking for room for", goal)
        for i, room in enumerate(self.rooms):
            #print("Room", room, self.rooms[room]["rates"])
            if goal in self.rooms[room]["rates"]:
                return room
        return -1
Ejemplo n.º 7
0
class Model(Model):
    def __init__(self, N, B, T, Treg, width, height):
        self.num_myelin = N * 8
        self.num_agents = N + B + T + Treg + self.num_myelin
        self.num_neurons = N
        self.num_myelin = 4
        self.num_limfocytB = B
        self.num_active_B = 0
        self.num_infected_B = 0
        self.num_limfocytT = T
        self.num_activeT = 0
        self.num_limfocytTreg = Treg
        self.grid = MultiGrid(width, height, True)
        self.schedule = RandomActivation(self)
        self.available_ids = set()
        self.dead_agents = set()
        self.new_agents = set()
        self.max_id = 0
        self.step_count = 1
        self.cytokina = 0
        self.cytokina_prev = 0
        self.sum = 0
        self.B = 0
        self.a = 0.80
        self.Ymax = 100
        open('new_and_dead.txt', 'w').close()
        # Create agents
        neuron_positions = [[3, 3], [3, 10], [3, 20], [3, 27], [10, 3],
                            [10, 10], [10, 20], [10, 27], [19, 3], [19, 10],
                            [19, 20], [19, 27], [26, 3], [26, 10], [26, 20],
                            [26, 27], [14, 15]]
        for i in range(self.num_neurons):
            a = Neuron(i, self, "Neuron")
            self.schedule.add(a)
            #Add agent to a random grid cell
            #x=self.random.randrange(self.grid.width)
            #y=self.random.randrange(self.grid.height)
            #self.grid.place_agent(a,(x,y))
            pos = neuron_positions[i]
            self.grid.place_agent(a, (pos[0], pos[1]))
            cells = self.grid.get_neighborhood(a.pos, True, False, 1)
            id = self.num_agents - i * 8
            for cell in cells:
                m = Myelin(id, self, "Myelin")
                self.schedule.add(m)
                self.grid.place_agent(m, cell)
                id -= 1

        #dodawanie różnych typów agentów zgodnie z ich liczbą podaną przy inicjacji modelu
        for i in range(self.num_limfocytB):
            a = LimfocytB(i + self.num_neurons, self, "LimfocytB")
            self.schedule.add(a)
            #Add agent to a random grid cell
            x = self.random.randrange(self.grid.width)
            y = self.random.randrange(self.grid.height)
            self.grid.place_agent(a, (x, y))
        for i in range(self.num_limfocytT):
            a = LimfocytT(i + N + B, self, "LimfocytT")
            self.schedule.add(a)
            #Add agent to a random grid cell
            x = self.random.randrange(self.grid.width)
            y = self.random.randrange(self.grid.height)
            self.grid.place_agent(a, (x, y))
        for i in range(self.num_limfocytTreg):
            a = LimfocytTreg(i + N + B + T, self, "LimfocytTreg")
            self.schedule.add(a)
            #Add agent to a random grid cell
            x = self.random.randrange(self.grid.width)
            y = self.random.randrange(self.grid.height)
            self.grid.place_agent(a, (x, y))

        self.max_id = self.num_agents - 1

        self.datacollector_population = DataCollector(
            model_reporters={"Populacja": compute_population})
        self.datacollector_T_population = DataCollector(
            model_reporters={"Populacja Limfocytów T": T_population})
        #self.datacollector_T_precentage=DataCollector(
        #model_reporters={"Precentage Limfocyt T": T_popualtion_precentage})
        self.datacollector_B_population = DataCollector(
            model_reporters={"Populacja Limfocytów B": B_population})
        self.datacollector_Treg_population = DataCollector(
            model_reporters={"Populacja Limfocytów Treg": Treg_population})
        self.datacollector_B_active_population = DataCollector(
            model_reporters={
                "Populacja Aktywnych Limfocytów B": B_activated_population
            })
        self.datacollector_T_active_population = DataCollector(
            model_reporters={
                "Populacja Aktywnych Limfocytów T": T_activated_population
            })

        self.datacollector_B_infected_population = DataCollector(
            model_reporters={
                "Populacja Zainfekowanych Limfocytów B": B_infected_population
            })

        self.datacollector_myelin_population = DataCollector(
            model_reporters={
                "Populacja osłonek mielinowych": myelin_population
            })

        self.datacollector_myelin_healths = DataCollector(
            model_reporters={
                "Suma punktów życia osłonek mielinowych": myelin_healths
            })

    def step(self):
        #print("self running: "+str(self.running()))

        self.schedule.step()
        self.datacollector_population.collect(self)
        self.datacollector_T_population.collect(self)
        #self.datacollector_T_precentage.collect(self)
        self.datacollector_B_population.collect(self)
        self.datacollector_Treg_population.collect(self)
        self.datacollector_B_active_population.collect(self)
        self.datacollector_T_active_population.collect(self)
        self.datacollector_B_infected_population.collect(self)
        self.datacollector_myelin_population.collect(self)
        self.datacollector_myelin_healths.collect(self)
        self.adding_removing()

        self.datacollector_myelin_healths.get_model_vars_dataframe().to_csv(
            r'Data/myelin_healths25.txt', sep=' ', mode='w')
        self.datacollector_T_population.get_model_vars_dataframe().to_csv(
            r'Data/T_population25.txt', sep=' ', mode='w')
        self.datacollector_B_population.get_model_vars_dataframe().to_csv(
            r'Data/B_population25.txt', sep=' ', mode='w')
        self.datacollector_Treg_population.get_model_vars_dataframe().to_csv(
            r'Data/Treg_population25.txt', sep=' ', mode='w')
        self.datacollector_B_active_population.get_model_vars_dataframe(
        ).to_csv(r'Data/B_active_population25.txt', sep=' ', mode='w')
        self.datacollector_T_active_population.get_model_vars_dataframe(
        ).to_csv(r'Data/T_active_population25.txt', sep=' ', mode='w')
        self.datacollector_B_infected_population.get_model_vars_dataframe(
        ).to_csv(r'Data/B_infected_population25.txt', sep=' ', mode='w')

        print("Liczba agentów: " + str(self.num_agents))
        print("MaxID: " + str(self.max_id))

        self.cytokina = max(
            min((self.B + self.cytokina_prev), self.Ymax) * self.a, 0)

        print("Cytokina " + str(self.cytokina))
        print("Cytokina_prev " + str(self.cytokina_prev))

        f = open("agents.txt", 'a')
        f.write("======Step : " + str(self.step_count) + "\n")
        for agent in self.schedule.agents:
            f.write("Agent: " + str(agent.type) + " " + str(agent.unique_id) +
                    str(agent.pos) + "\n")

        f.close()
        self.cytokina_prev = self.cytokina
        self.B = 0

    def running(self):
        self.step()

    def adding_removing(
            self):  #funckja odpowiedzialna za dodawanie i usuwanie agentów
        #print("AddingRemoving")
        f = open("new_and_dead.txt", 'a')
        f.write("Step " + str(self.step_count) + "\n")
        f.write("======Dead agents======: " + "\n")
        for d in self.dead_agents:
            try:
                self.schedule.remove(d)
                self.num_agents -= 1
                self.available_ids.add(d.unique_id)
            except KeyError:
                continue
            try:
                self.grid._remove_agent(d.pos, d)
            except KeyError:
                continue
            f.write(str(d.unique_id) + " " + d.type + "\n")
            #if d.type=="AktywowanyLimfocytT":
            #    self.cytokina-=1
        self.dead_agents.clear()
        f.write("======New Agents=====: " + "\n")
        for n in self.new_agents:
            self.schedule.add(n)
            self.num_agents += 1
            self.grid.place_agent(n, n.pos)
            if n.unique_id in self.available_ids:
                self.available_ids.remove(n.unique_id)
            f.write(str(n.unique_id) + " " + n.type + "\n")
            #if n.type=="AktywowanyLimfocytT":
            #   self.cytokina+=1
        self.new_agents.clear()
        m = 1
        n = 0
        for agent in self.schedule.agents:
            if agent.unique_id > m:
                m = agent.unique_id
            if (agent.type == "LimfocytT") or (agent.type
                                               == "AktywowanyLimfocytT"):
                n += 1
        self.max_id = m
        self.num_limfocytT = 0
        self.deficiencies()
        f.close()

    def deficiencies(self):
        n = B_population(self)
        if n == 0:
            if len(self.available_ids) == 0:
                self.max_id += 1
                id = self.max_id
            else:
                id = min(self.available_ids)
                self.available_ids.remove(id)
            agent = LimfocytB(id, self, "LimfocytB")
            self.schedule.add(agent)
            x = self.random.randrange(self.grid.width)
            y = self.random.randrange(self.grid.height)
            self.grid.place_agent(agent, (x, y))
            self.num_agents += 1
        n = T_population(self)
        if n == 0:
            for i in range(10):
                if len(self.available_ids) == 0:
                    self.max_id += 1
                    id = self.max_id
                else:
                    id = min(self.available_ids)
                    self.available_ids.remove(id)
                agent = LimfocytB(id, self, "LimfocytT")
                self.schedule.add(agent)
                x = self.random.randrange(self.grid.width)
                y = self.random.randrange(self.grid.height)
                self.grid.place_agent(agent, (x, y))
                self.num_agents += 1
        n = Treg_population(self)
        if n == 0:
            if len(self.available_ids) == 0:
                self.max_id += 1
                id = self.max_id
            else:
                id = min(self.available_ids)
                self.available_ids.remove(id)
            agent = LimfocytB(id, self, "LimfocytTreg")
            self.schedule.add(agent)
            x = self.random.randrange(self.grid.width)
            y = self.random.randrange(self.grid.height)
            self.grid.place_agent(agent, (x, y))
            self.num_agents += 1
Ejemplo n.º 8
0
class Themepark(Model):
    def __init__(self, N_attr, N_cust, width, height, strategy, theme, max_time, weight, adaptive):
        self.theme = theme
        self.max_time = max_time
        self.N_attr = N_attr
        self.penalty_per = PENALTY_PERCENTAGE
        self.weight = weight
        self.adaptive = adaptive
        self.strategies = STRATEGIES
        self.x_list, self.y_list, self.positions = xlist, ylist, positions
        self.x_list, self.y_list, self.positions = get_attraction_coordinates(WIDTH, HEIGHT, self.N_attr, theme)
        self.happinesses = []
        self.starting_positions = [[int((WIDTH/2)-1), 0], [int(WIDTH/2), 0], [int((WIDTH/2)+1), 0]]
        self.path_coordinates = get_coordinates(WIDTH, HEIGHT, NUM_OBSTACLES, self.N_attr, theme)
        self.N_attr = N_attr    # num of attraction agents
        self.N_cust = N_cust    # num of customer agents
        self.width = width
        self.height = height
        self.total_steps = 0
        self.cust_ids = N_cust
        self.strategy = strategy
        self.grid = MultiGrid(width, height, torus=False)
        self.schedule = BaseScheduler(self)
        self.schedule_Attraction = BaseScheduler(self)
        self.schedule_Customer = BaseScheduler(self)
        self.totalTOTAL = 0
        self.attractions = self.make_attractions()
        self.attraction_history = self.make_attr_hist()
        self.running = True
        self.data = []
        self.data_customers = []
        self.park_score = []
        self.data_dict = {}
        self.hist_random_strat = []
        self.hist_close_strat = []
        self.all_rides_list = []
        self.strategy_composition = self.make_strategy_composition()
        self.memory = 5
        self.customer_score = []
        self.customers = self.add_customers(self.N_cust)
        self.only_random = False


        for attraction in self.get_attractions():
            self.data_dict[attraction.unique_id] = ({
                               "id": attraction.unique_id,
                               "length": attraction.attraction_duration,
                               "waiting_list": []})

        if len(self.strategies) == 6:
            self.datacollector = DataCollector(

                {"Random": lambda m: self.strategy_counter(self.strategies[0]),
                "0.00": lambda m: self.strategy_counter(self.strategies[1]),
                "0.25": lambda m: self.strategy_counter(self.strategies[2]),
                "0.50": lambda m: self.strategy_counter(self.strategies[3]),
                "0.75": lambda m: self.strategy_counter(self.strategies[4]),
                "1.00": lambda m: self.strategy_counter(self.strategies[5]),
                })
        else:
            self.datacollector = DataCollector(
                {"0.00": lambda m: self.strategy_counter(self.strategies[0]),
                "0.25": lambda m: self.strategy_counter(self.strategies[1]),
                "0.50": lambda m: self.strategy_counter(self.strategies[2]),
                "0.75": lambda m: self.strategy_counter(self.strategies[3]),
                "1.00": lambda m: self.strategy_counter(self.strategies[4]),
                })

        self.datacollector2 = DataCollector(
            {"score": lambda m: self.make_score()})

        self.total_waited_time = 0

        self.monitor = Monitor(self.max_time, self.N_attr, self.positions)

    def make_score(self):
        ideal = {}
        cust_in_row = 0
        for i in range(len(self.get_attractions())):
            ideal[i] = self.N_cust/self.N_attr
            cust_in_row += self.get_attractions()[i].N_current_cust

        tot_difference = 0
        for i in range(len(self.get_attractions())):

            difference = abs(cust_in_row/self.N_attr  - self.get_attractions()[i].N_current_cust)
            tot_difference += difference

        fraction_not_right = (tot_difference/self.N_cust)
        return abs(1-(fraction_not_right)) * cust_in_row/self.N_cust

    def make_attr_hist(self):
        attraction_history = {}
        for attraction in self.get_attractions():

            attraction_history[attraction] = [0] * (self.max_time + 1)
        return attraction_history

    def strategy_counter(self, strategy):
        counter_total = {}

        for attraction_pos in self.positions:

            agents = self.grid.get_neighbors(
                attraction_pos,
                moore=True,
                radius=0,
                include_center=True
            )

            counter = 0
            for agent in self.customers:
                if agent.weight == strategy:
                    counter += 1

        return counter

    def make_strategy_composition(self):
        if self.strategy == "Random_test_4":
            self.strategies = ["Random_test_4", 0.0, 0.25, 0.50, 0.75, 1.0]
            dict = {self.strategies[0]: 1/6, self.strategies[1]:0.20, self.strategies[2]:0.20,
                            self.strategies[3]:0.20, self.strategies[4]:0.20, self.strategies[5]: 0.20}

            composition_list = []
            for i in range(len(self.strategies)):
                if i == 0:
                    dict[self.strategies[i]] = FRACTION_RANDOM
                    continue
                else:
                    composition_list.append(random.randint(0,100))
            sum_comp = sum(composition_list)

            sum_comp = sum_comp - sum_comp * FRACTION_RANDOM
            for i in range(len(self.strategies)):
                if i == 0:
                    continue
                else:
                    dict[self.strategies[i]] = composition_list[i-1] /sum_comp

        else:
            dict = {self.strategies[0]: 0.20, self.strategies[1]:0.20, self.strategies[2]:0.20,
                            self.strategies[3]:0.20, self.strategies[4]:0.20}

            composition_list = []
            for i in range(len(self.strategies)):

                composition_list.append(random.randint(0,100))

            sum_comp = sum(composition_list)

            sum_comp = sum_comp
            for i in range(len(self.strategies)):

                dict[self.strategies[i]] = composition_list[i-1] /sum_comp



        return dict

    def make_attractions(self):
        """ Initialize attractions on fixed position."""

        attractions = {}
        for i in range(self.N_attr):

            pos = (self.x_list[i], self.y_list[i])
            if self.grid.is_cell_empty(pos):

                name = str(i)
                a = Attraction(i, self, pos, name, self.N_cust, self.weight)
                attractions[i] = a

                self.schedule_Attraction.add(a)
                self.grid.place_agent(a, pos)
        return attractions

    def get_attractions(self):
        """
        Get a list with all attractions
        """
        agents = self.grid.get_neighbors(
            mid_point,
            moore=True,
            radius=RADIUS,
            include_center=True)

        attractions = []
        for agent in agents:
            if type(agent) == Attraction:
                attractions.append(agent)

        return attractions

    def add_customers(self, N_cust, added=False):
        """ Initialize customers on random positions."""

        weights_list = []
        if self.adaptive is True:

            for j in self.strategy_composition.keys():
                for i in range(round(N_cust*self.strategy_composition[j])):
                    weights_list.append(j)

            if len(weights_list) < self.N_cust:
                rand = random.choice(self.strategies)
                weights_list.append(rand)
            elif len(weights_list) > self.N_cust:
                rand = random.choice(weights_list)
                weights_list.remove(rand)


        else:
            if self.strategy is not "Random":

                # do what normally is done
                for i in range(round(N_cust)):
                    weights_list.append(self.weight)


        cust_list = []
        # weight_counter = 0
        # pick_weight = 0
        for i in range(N_cust):

            # pos_temp = random.choice(self.starting_positions)
            pos_temp = [random.randint(0,WIDTH-1), random.randint(0,HEIGHT-1)]
            rand_x, rand_y = pos_temp[0], pos_temp[1]

            pos = (rand_x, rand_y)

            if added is True:
                i = self.cust_ids
            if self.strategy == "Random_test_4":
                if weights_list[i] == "Random_test_4":
                    strategy = "Random_test_4"
                else:
                    strategy = "Closest_by"
            else:
                strategy = self.strategy


            # Deze if is omdat bij alleen random self.weight none is!
            if weights_list == []:
                weight = None
            else:
                weight = weights_list[i]
            a = Customer(i, self, pos, self.x_list, self.y_list, self.positions, strategy, weight, self.adaptive)

            self.schedule_Customer.add(a)

            self.grid.place_agent(a, pos)
            cust_list.append(a)

        return cust_list

    def calculate_people(self):
        """Calculate how many customers are in which attraction."""

        counter_total = {}

        for attraction_pos in self.positions:

            agents = self.grid.get_neighbors(
                attraction_pos,
                moore=True,
                radius=0,
                include_center=True
            )

            counter = 0
            for agent in agents:
                if type(agent) is Customer:
                    counter += 1
                else:
                    attraction = agent

            attraction.N_current_cust = counter
            counter_total[attraction.unique_id] = counter

        return list(counter_total.values())

    def calc_waiting_time(self):

        counter_total = {}

        attractions = self.get_attractions()
        for attraction in attractions:

            counter_total[attraction.unique_id] = attraction.current_waitingtime

        return counter_total


    def calculate_people_sorted(self):
        """
        Calculate how many customers are in which attraction.
        Returns a SORTED LIST.
        For example: indexes = [3, 2, 5, 1, 4]
        indicates that attraction3 has the least people waiting.
        """

        counter_total = {}

        for attraction_pos in self.positions:

            agents = self.grid.get_neighbors(
                attraction_pos,
                moore=True,
                radius=0,
                include_center=True
            )

            counter = 0
            for agent in agents:
                if type(agent) is Customer:
                    counter += 1
                else:
                    attraction = agent

            attraction.N_current_cust = counter
            self.attraction_history[attraction][self.totalTOTAL] = counter
            counter_total[attraction.unique_id] = counter
        return counter_total

    def make_route(self):
        """Draw coordinates of a possible path."""

        for i in range(len(self.path_coordinates)):
            pos = self.path_coordinates[i]

            if pos not in self.positions:

                # Create path agent
                path = Route(i, self, pos)
                self.schedule.add(path)

                self.grid.place_agent(path, pos)

    def get_themepark_score(self):
        """
        Get score of a themepark based on:
            - A total of all waitingtimes for every customer
            - TODO
        """
        attractions = self.get_attractions()
        total_wait, total_rides = 0, 0
        for attraction in attractions:
            total_wait += attraction.current_waitingtime

            if attraction.current_a is not None:
                total_rides += 1

        if total_rides == 0:
            return total_rides

        return (total_wait / total_rides)

    def get_strategy_history(self):
        """ Update history with how many customers chose which strategy """

        customers = self.get_customers()
        randomstrat, closebystrat = 0, 0

        for customer in customers:
            if customer.strategy == "Random" or customer.strategy == "Random_test_4":
                randomstrat += 1
            elif customer.strategy == "Closest_by":
                closebystrat += 1

        self.hist_random_strat.append(randomstrat)
        self.hist_close_strat.append(closebystrat)

    def get_customers(self):
        agents = self.grid.get_neighbors(
            mid_point,
            moore=True,
            radius=RADIUS,
            include_center=True)

        customers = []

        # Count customer agents
        for agent in agents:
            if type(agent) == Customer:
                customers.append(agent)
        return customers

    def get_data_customers(self):
        """ Return dictionary with data of customers """

        data = {}
        agents = self.grid.get_neighbors(
            mid_point,
            moore=True,
            radius=RADIUS,
            include_center=True)

        for agent in agents:
            if type(agent) is Customer:
                data[agent.unique_id] = {
                "totalwaited": agent.total_ever_waited,
                "visited_attractions": agent.nmbr_attractions,
                "strategy": agent.strategy,
                "swapped_strat": agent.strategy_swap_hist
                }
        return data

    def calc_hapiness(self):
        """
        Calculate mean hapiness of all customers, based on:

        - How many rides were taken
        - Number of times in the same attraction
        - Total waiting time
        """
        customers = self.get_customers()

        scores = []



        for customer in customers:
            history = customer.history
            values = list(history.values())
            total_rides = sum(values)

            if total_rides != 0:
                scores.append(total_rides / self.N_attr - self.totalTOTAL / customer.total_ever_waited)
            else:
                return None

        scores = np.interp(scores, (min(scores), max(scores)), (1, 10))
        return np.mean(scores)

    def get_history_list(self):

        customers = self.get_customers()


        histories = {}

        for customer in customers:
            history = customer.history
            values = list(history.values())
            histories[customer.unique_id] = values
        return histories

    def final(self):
        """ Return data """
        hist_list = []
        agents = self.grid.get_neighbors(
            mid_point,
            moore=True,
            radius=RADIUS,
            include_center=True)

        attractions = self.get_attractions()
        self.all_rides_list = [0] * len(attractions[0].in_attraction_list)
        for attraction in attractions:
            for i in range(len(attraction.in_attraction_list)):
                self.all_rides_list[i] += attraction.in_attraction_list[i]

        for i in range(len(self.all_rides_list)):
            self.all_rides_list[i] /= self.N_attr
        print("ALL RIDES LIST", self.all_rides_list)

        cust_data = self.get_data_customers()
        for agent in agents:
            if type(agent) is Customer:
                sum_attr = sum(agent.history.values())
                if sum_attr > 0:
                    hist_list.append(agent.strategy_swap_hist)
                else:
                    hist_list.append(agent.strategy_swap_hist)
                # print("swap:",agent.strategy_swap_hist , "sum:",sum_attr)


        # plt.hist(hist_list)
        # plt.show()

        histories = self.get_history_list()

        # save data
        try:
            pickle.dump(self.datacollector.get_model_vars_dataframe(), open("../data/strategy_history.p", 'wb'))
            pickle.dump(self.datacollector2.get_model_vars_dataframe(), open("../data/eff_score_history.p", 'wb'))
            pickle.dump(cust_data, open("../data/customers.p", 'wb'))
            pickle.dump(self.park_score[-1], open("../data/park_score.p", "wb"))
            pickle.dump(self.happinesses, open("../data/hapiness.p", "wb"))
            pickle.dump(histories, open("../data/cust_history.p", 'wb'))
        except:
            pickle.dump(self.datacollector.get_model_vars_dataframe(), open("data/strategy_history.p", 'wb'))
            pickle.dump(self.datacollector2.get_model_vars_dataframe(), open("data/eff_score_history.p", 'wb'))
            pickle.dump(cust_data, open("data/customers.p", 'wb'))
            pickle.dump(self.park_score[-1], open("data/park_score.p", "wb"))
            pickle.dump(self.happinesses, open("data/hapiness.p", "wb"))
            pickle.dump(histories, open("data/cust_history.p", 'wb'))

        try:
            pickle.dump(self.all_rides_list, open("../data/all_rides.p", "wb"))
        except:
            pickle.dump(self.all_rides_list, open("data/all_rides.p", "wb"))

        print()
        print("RUN HAS ENDED")
        print()


    def save_data(self):
        """Save data of all attractions and customers."""

        # Get info
        waitinglines = self.calc_waiting_time()

        for i in range(len(self.attractions)):
            self.data_dict[i]["waiting_list"].append(waitinglines.get(i))

        self.park_score.append(sum(waitinglines.values()))
        self.happinesses.append(self.calc_hapiness())

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

        if self.totalTOTAL < self.max_time:
            self.totalTOTAL += 1
            self.schedule.step()
            self.datacollector.collect(self)
            self.datacollector2.collect(self)

            self.schedule_Attraction.step()

            self.schedule_Customer.step()

            # update memory of attractions
            # attractions = self.get_attractions()
            # for attraction in attractions:
            #     attraction.update_memory()

            self.total_steps += 1

            self.save_data()
            self.get_strategy_history()

        else:
            for key in self.attraction_history.keys():
                y = self.attraction_history[key]
                x = list(range(0, self.max_time))
            self.final()
Ejemplo n.º 9
0
class PolicyEmergenceSM(Model):

	'''
	Simplest Model for the policy emergence model.
	'''

	def __init__(self, SM_inputs, height=20, width=20):

		self.height = height
		self.width = width

		self.SM_inputs = SM_inputs

		self.stepCount = 0
		self.agenda_PC = None
		self.agenda_PF = None
		self.policy_implemented = None
		self.policy_implemented_number = None
		self.policy_formulation_run = False  # True if an agenda is found

		self.schedule = RandomActivation(self)
		self.grid = SingleGrid(height, width, torus=True)

		# creation of the datacollector vector
		self.datacollector = DataCollector(
			# Model-level variables
			model_reporters =  {
				"step": "stepCount",
				"AS_PF": get_problem_policy_chosen,
				"agent_attributes": get_agents_attributes},
			# Agent-level variables
			agent_reporters = {
				"x": lambda a: a.pos[0],
				"y": lambda a: a.pos[1],
				"Agent type": lambda a:type(a), 
				"Issuetree": lambda a: getattr(a, 'issuetree', [None])[a.unique_id if isinstance(a, ActiveAgent) else 0]}
			)

		# , "agenda_PC":"agenda_PC", "agenda_PF":"agenda_PF", "policy_implemented": "policy_implemented"

		# "x": lambda a: a.pos[0], "y": lambda a: a.pos[1]
		# "z": lambda a:a.issuetree

		# belief tree properties
		self.len_S, self.len_PC, self.len_DC, self.len_CR = issue_tree_input(self)
		# print(self.len_S, self.len_PC, self.len_DC, self.len_CR)

		# issue tree properties
		self.policy_instruments, self.len_ins_1, self.len_ins_2, self.len_ins_all, self.PF_indices = policy_instrument_input(self, self.len_PC)

		# Set up active agents
		init_active_agents(self, self.len_S, self.len_PC, self.len_DC, self.len_CR, self.len_PC, self.len_ins_1, self.len_ins_2, self.len_ins_all, self.SM_inputs)

		# Set up passive agents
		init_electorate_agents(self, self.len_S, self.len_PC, self.len_DC, self.SM_inputs)

		# Set up truth agent
		init_truth_agent(self, self.len_S, self.len_PC, self.len_DC, self.len_ins_1, self.len_ins_2, self.len_ins_all)
		# the issue tree will need to be updated at a later stage witht he values from the system/policy context

		# print("Schedule has : ", len(self.schedule.agents), " agents.")
		# print(self.schedule.agents)
		# print(" ")

		# for agent in self.schedule.agent_buffer(shuffled=False):
		# 	print(' ')
		# 	print(agent)
		# 	print(type(agent))
		# 	if isinstance(agent, ActiveAgent):
		# 		print(agent.unique_id, " ", agent.pos, " ", agent.agent_type, " ", agent.resources, " ", agent.affiliation, " ", agent.issuetree[agent.unique_id], " ", agent.policytree[agent.unique_id][0])
		# 	if isinstance(agent, ElectorateAgent):
		# 		print(agent.unique_id, " ", agent.pos, " ", agent.affiliation, " ", agent.issuetree)
		# 	if isinstance(agent, TruthAgent):
		# 		print(agent.pos, " ", agent.issuetree)

		self.running = True
		self.numberOfAgents = self.schedule.get_agent_count()
		self.datacollector.collect(self)

	def step(self, KPIs):
		print(" ")
		print("Step +1 - Policy emergence model")
		print("Step count: ", self.stepCount)

		'''
		Main steps of the Simplest Model for policy emergence:
		0. Module interface - Input
			Obtention of the beliefs from the system/policy context
			!! This is to be implemented at a later stage
		1. Agenda setting step
		2. Policy formulation step
		3. Module interface - Output
			Implementation of the policy instrument selected
		'''

		# saving the attributes
		self.KPIs = KPIs

		# 0.
		self.module_interface_input(self.KPIs)

		'''
		TO DO:
		- Introduce the transfer of information between the external parties and the truth agent relates to the policy impacts
		'''

		# 1.
		self.agenda_setting()

		# 2.
		if self.policy_formulation_run:
			self.policy_formulation()
		else:
			self.policy_implemented = self.policy_instruments[-1]

		# 3.
		# self.module_interface_output()

		# end of step actions:
		# iterate the steps counter
		self.stepCount += 1

		# collect data
		self.datacollector.collect(self)

		print("step ends")
		print(" ")

		# print(self.datacollector.get_agent_vars_dataframe())
		print(self.datacollector.get_model_vars_dataframe())

		return self.policy_implemented

	def module_interface_input(self, KPIs):

		'''
		The module interface input step consists of actions related to the module interface and the policy emergence model

		Missing:
		- Electorate actions
		'''

		# selection of the Truth agent policy tree and issue tree
		for agent in self.schedule.agent_buffer(shuffled=True):
			if isinstance(agent, TruthAgent):
				truth_policytree = agent.policytree_truth
				for issue in range(self.len_DC+self.len_PC+self.len_S):
					agent.issuetree_truth[issue] = KPIs[issue]
				truth_issuetree = agent.issuetree_truth

		# Transferring policy impact to active agents
		for agent in self.schedule.agent_buffer(shuffled=True):
			if isinstance(agent, ActiveAgent):
				# replacing the policy family likelihoods
				for PFj in range(self.len_PC):
					for PFij in range(self.len_PC):
						agent.policytree[agent.unique_id][PFj][PFij] = truth_policytree[PFj][PFij]

				# replacing the policy instruments impacts
				for insj in range(self.len_ins_1 + self.len_ins_2 + self.len_ins_all):
					agent.policytree[agent.unique_id][self.len_PC+insj][0:self.len_S] = truth_policytree[self.len_PC+insj]

				# replacing the issue beliefs from the KPIs
				for issue in range(self.len_DC+self.len_PC+self.len_S):
					agent.issuetree[agent.unique_id][issue][0] = truth_issuetree[issue]
				self.preference_update(agent, agent.unique_id)

	def agenda_setting(self):

		'''
		The agenda setting step is the first step in the policy process conceptualised in this model. The steps are given as follows:
		1. Active agents policy core issue selection
		2. Active agents policy family selection
		3. Active agents actions [to be detailed later]
		4. Active agents policy core issue selection update
		5. Active agents policy family selection update
		6. Agenda selection
		'''

		# 1. & 2.
		for agent in self.schedule.agent_buffer(shuffled=False):
			if isinstance(agent, ActiveAgent):  # considering only active agents
				agent.selection_PC()
				agent.selection_PF()
				# print("PC and PF selected for  agent", agent.unique_id, ": ", agent.selected_PC, agent.selected_PF)

		# 3.

		# 4. & 5.
		for agent in self.schedule.agent_buffer(shuffled=False):
			if isinstance(agent, ActiveAgent):  # considering only active agents
				agent.selection_PC()
				agent.selection_PF()

		# 6. 
		# All active agents considered
		selected_PC_list = []
		selected_PF_list = []
		number_ActiveAgents = 0
		for agent in self.schedule.agent_buffer(shuffled=False):
			if isinstance(agent, ActiveAgent):  # considering only policy makers
				selected_PC_list.append(agent.selected_PC)
				selected_PF_list.append(agent.selected_PF)
				number_ActiveAgents += 1

		# finding the most common policy core issue and its frequency
		d = defaultdict(int)
		for i in selected_PC_list:
			d[i] += 1
		result = max(d.items(), key=lambda x: x[1])
		agenda_PC_temp = result[0]
		agenda_PC_temp_frequency = result[1]

		# finding the most common policy family issue and its frequency
		d = defaultdict(int)
		for i in selected_PF_list:
			d[i] += 1
		result = max(d.items(), key=lambda x: x[1])
		agenda_PF_temp = result[0]
		agenda_PF_temp_frequency = result[1]

		# checking for majority
		if agenda_PC_temp_frequency > int(number_ActiveAgents/2) and agenda_PF_temp_frequency > int(number_ActiveAgents/2):
			self.agenda_PC = agenda_PC_temp
			self.agenda_PF = agenda_PF_temp
			self.policy_formulation_run = True
			print("The agenda consists of PC", self.agenda_PC, " and PF", self.agenda_PF, ".")
		else:
			self.policy_formulation_run = False
			print("No agenda was formed, moving to the next step.")

	def policy_formulation(self):

		'''
		The policy formulation step is the second step in the policy process conceptualised in this model. The steps are given as follows:
		0. Detailing of policy instruments that can be considered
		1. Active agents deep core issue selection
		2. Active agents policy instrument selection
		3. Active agents actions [to be detailed later]
		4. Active agents policy instrument selection update
		5. Policy instrument selection

		NOTE: THIS CODE DOESNT CONSIDER MAJORITY WHEN MORE THAN THREE POLICY MAKERS ARE INCLUDED, IT CONSIDERS THE MAXIMUM. THIS NEEDS TO BE ADAPTED TO CONSIDER 50% OR MORE!
		'''

		print("Policy formulation being introduced")

		# 0.
		possible_PI = self.PF_indices[self.agenda_PF]

		# 1. & 2.
		for agent in self.schedule.agent_buffer(shuffled=False):
			if isinstance(agent, ActiveAgent):  # considering only active agents
				agent.selection_S()
				agent.selection_PI()

		# 3.

		# 4. & 5.
		for agent in self.schedule.agent_buffer(shuffled=False):
			if isinstance(agent, ActiveAgent):  # considering only active agents
				agent.selection_PI()

		# 6. 
		# Only policy makers considered
		selected_PI_list = []
		number_PMs = 0
		for agent in self.schedule.agent_buffer(shuffled=False):
			if isinstance(agent, ActiveAgent) and agent.agent_type == 'policymaker':  # considering only policy makers
				selected_PI_list.append(agent.selected_PI)
				number_PMs += 1

		# finding the most common secondary issue and its frequency
		d = defaultdict(int)
		for i in selected_PI_list:
			d[i] += 1
		result = max(d.items(), key=lambda x: x[1])
		self.policy_implemented_number = result[0]
		policy_implemented_number_frequency = result[1]

		# check for the majority and implemented if satisfied
		if policy_implemented_number_frequency > int(number_PMs/2):
			print("The policy instrument selected is policy instrument ", self.policy_implemented_number, ".")
			self.policy_implemented = self.policy_instruments[self.policy_implemented_number]
		else:
			print("No consensus on a policy instrument.")
			self.policy_implemented = self.policy_instruments[-1] # selecting last policy instrument which is the no instrument policy instrument

	def module_interface_output(self):

		print("Module interface output not introduced yet")

	def preference_update(self, agent, who):

		self.preference_update_DC(agent, who)

		self.preference_update_PC(agent, who)

		self.preference_update_S(agent, who)

	def preference_update_DC(self, agent, who):

		"""
		The preference update function (DC)
		===========================

		This function is used to update the preferences of the deep core issues of agents in their
		respective belief trees.

		agent - this is the owner of the belief tree
		who - this is the part of the belieftree that is considered - agent.unique_id should be used for this - this is done to also include partial knowledge preference calculation

		"""	

		len_DC = self.len_DC
		len_PC = self.len_PC
		len_S = self.len_S

		#####
		# 1.5.1. Preference calculation for the deep core issues

		# 1.5.1.1. Calculation of the denominator
		PC_denominator = 0
		for h in range(len_DC):
			if agent.issuetree[who][h][1] == None or agent.issuetree[who][h][0] == None:
				PC_denominator = 0
			else:
				PC_denominator = PC_denominator + abs(agent.issuetree[who][h][1] - agent.issuetree[who][h][0])
		# print('The denominator is given by: ' + str(PC_denominator))

		# 1.5.1.2. Selection of the numerator and calculation of the preference
		for i in range(len_DC):
			# There are rare occasions where the denominator could be 0
			if PC_denominator != 0:
				agent.issuetree[who][i][2] = abs(agent.issuetree[who][i][1] - agent.issuetree[who][i][0]) / PC_denominator
			else:
				agent.issuetree[who][i][2] = 0

	def preference_update_PC(self, agent, who):

		"""
		The preference update function (PC)
		===========================

		This function is used to update the preferences of the policy core issues of agents in their
		respective belief trees.

		agent - this is the owner of the belief tree
		who - this is the part of the belieftree that is considered - agent.unique_id should be used for this - this is done to also include partial knowledge preference calculation

		"""	

		len_DC = self.len_DC
		len_PC = self.len_PC
		len_S = self.len_S

		#####	
		# 1.5.2 Preference calculation for the policy core issues
		PC_denominator = 0
		# 1.5.2.1. Calculation of the denominator
		for j in range(len_PC):
			# print('Selection PC' + str(j+1))
			# print('State of the PC' + str(j+1) + ': ' + str(agent.issuetree[0][len_DC + j][0])) # the state printed
			# Selecting the causal relations starting from PC
			for k in range(len_DC):
				# Contingency for partial knowledge issues
				if agent.issuetree[who][k][1] == None or agent.issuetree[who][k][0] == None or agent.issuetree[who][len_DC+len_PC+len_S+j+(k*len_PC)][0] == None:
					PC_denominator += 0
				else:
					# print('Causal Relation PC' + str(j+1) + ' - PC' + str(k+1) + ': ' + str(agent.issuetree[0][len_DC+len_PC+len_S+j+(k*len_PC)][1]))
					# print('Gap of PC' + str(k+1) + ': ' + str((agent.issuetree[0][k][1] - agent.issuetree[0][k][0])))
					# Check if causal relation and gap are both positive of both negative
					# print('agent.issuetree[' + str(who) + '][' + str(len_DC+len_PC+len_S+j+(k*len_PC)) + '][0]: ' + str(agent.issuetree[who][len_DC+len_PC+len_S+j+(k*len_PC)][0]))
					if (agent.issuetree[who][len_DC+len_PC+len_S+j+(k*len_PC)][0] < 0 and (agent.issuetree[who][k][1] - agent.issuetree[who][k][0]) < 0) or (agent.issuetree[who][len_DC+len_PC+len_S+j+(k*len_PC)][0] > 0 and (agent.issuetree[who][k][1] - agent.issuetree[who][k][0]) > 0):
						PC_denominator = PC_denominator + abs(agent.issuetree[who][len_DC+len_PC+len_S+j+(k*len_PC)][0]*(agent.issuetree[who][k][1] - agent.issuetree[who][k][0]))
						# print('This is the PC numerator: ' + str(PC_denominator))
					else:
						PC_denominator = PC_denominator	

		# 1.5.2.2. Addition of the gaps of the associated mid-level issues
		for i in range(len_PC):
			# Contingency for partial knowledge issues
			if agent.issuetree[who][len_DC + i][1] == None or agent.issuetree[who][len_DC + i][0] == None:
				PC_denominator = PC_denominator
			else:
				# print('This is the gap for the PC' + str(i+1) + ': ' + str(agent.issuetree[0][len_DC + i][1] - agent.issuetree[0][len_DC + i][0]))
				PC_denominator += abs(agent.issuetree[who][len_DC + i][1] - agent.issuetree[who][len_DC + i][0])
		# print('This is the S denominator: ' + str(PC_denominator))
		
		# 1.5.2.3 Calculation the numerator and the preference
		# Select one by one the PC
		for j in range(len_PC):

			# 1.5.2.3.1. Calculation of the right side of the numerator
			PC_numerator = 0
			# print('Selection PC' + str(j+1))
			# print('State of the PC' + str(j+1) + ': ' + str(agent.issuetree[0][len_DC + j][0])) # the state printed
			# Selecting the causal relations starting from DC
			for k in range(len_DC):
				# Contingency for partial knowledge issues
				if agent.issuetree[who][k][1] == None or agent.issuetree[who][k][0] == None or agent.issuetree[who][len_DC+len_PC+len_S+j+(k*len_PC)][0] == None:
					PC_numerator += 0
				else:
					# print('Causal Relation PC' + str(j+1) + ' - DC' + str(k+1) + ': ' + str(agent.issuetree[0][len_DC+len_PC+len_S+j+(k*len_PC)][1]))
					# print('Gap of DC' + str(k+1) + ': ' + str((agent.issuetree[0][k][1] - agent.issuetree[0][k][0])))
					# Check if causal relation and gap are both positive of both negative
					if (agent.issuetree[who][len_DC+len_PC+len_S+j+(k*len_PC)][0] < 0 and (agent.issuetree[who][k][1] - agent.issuetree[who][k][0]) < 0) or (agent.issuetree[who][len_DC+len_PC+len_S+j+(k*len_PC)][0] > 0 and (agent.issuetree[who][k][1] - agent.issuetree[who][k][0]) > 0):
						PC_numerator = PC_numerator + abs(agent.issuetree[who][len_DC+len_PC+len_S+j+(k*len_PC)][0]*(agent.issuetree[who][k][1] - agent.issuetree[who][k][0]))
						# print('This is the PC numerator: ' + str(PC_numerator))
					else:
						PC_numerator = PC_numerator	

			# 1.5.2.3.2. Addition of the gap to the numerator
			# Contingency for partial knowledge issues
			if agent.issuetree[who][len_DC + j][1] == None or agent.issuetree[who][len_DC + j][0] == None:
				PC_numerator += 0
			else:
				# print('This is the gap for the PC' + str(j+1) + ': ' + str(agent.issuetree[0][len_DC + j][1] - agent.issuetree[0][len_DC + j][0]))
				PC_numerator += abs(agent.issuetree[who][len_DC + j][1] - agent.issuetree[who][len_DC + j][0])
			# print('The numerator is equal to: ' + str(PC_numerator))
			# print('The denominator is equal to: ' + str(PC_denominator))

			# 1.5.2.3.3. Calculation of the preference
			if PC_denominator != 0:
				agent.issuetree[who][len_DC+j][2] = round(PC_numerator/PC_denominator,3) 
			# print('The new preference of the policy core PC' + str(j+1) + ' is: ' + str(agent.issuetree[0][len_DC+j][2]))
			else:
				agent.issuetree[who][len_DC+j][2] = 0

	def preference_update_S(self, agent, who):

		"""
		The preference update function (S)
		===========================

		This function is used to update the preferences of secondary issues the agents in their
		respective belief trees.

		agent - this is the owner of the belief tree
		who - this is the part of the belieftree that is considered - agent.unique_id should be used for this - this is done to also include partial knowledge preference calculation

		"""	

		len_DC = self.len_DC
		len_PC = self.len_PC
		len_S = self.len_S

		#####	
		# 1.5.3 Preference calculation for the secondary issues
		S_denominator = 0
		# 1.5.2.1. Calculation of the denominator
		for j in range(len_S):
			# print('Selection S' + str(j+1))
			# print('State of the S' + str(j+1) + ': ' + str(agent.issuetree[0][len_DC + len_PC + j][0])) # the state printed
			# Selecting the causal relations starting from S
			for k in range(len_PC):
				# Contingency for partial knowledge issues
				if agent.issuetree[who][len_DC + k][1] == None or agent.issuetree[who][len_DC + k][0] == None or agent.issuetree[who][len_DC+len_PC+len_S+len_DC*len_PC+j+(k*len_S)][0] == None:
					S_denominator += 0
				else:
					# print('Causal Relation S' + str(j+1) + ' - PC' + str(k+1) + ': ' + str(agent.issuetree[who][len_DC+len_PC+len_S+len_DC*len_PC+j+(k*len_S)][0]))
					# print('Gap of PC' + str(k+1) + ': ' + str((agent.issuetree[who][len_DC+k][1] - agent.issuetree[who][len_DC+k][0])))
					# Check if causal relation and gap are both positive of both negative
					# print('agent.issuetree[' + str(who) + '][' + str(len_DC+len_PC+len_S+len_DC*len_PC+j+(k*len_S)) + '][0]: ' + str(agent.issuetree[who][len_DC+len_PC+len_S+len_DC*len_PC+j+(k*len_S)][0]))
					if (agent.issuetree[who][len_DC+len_PC+len_S+len_DC*len_PC+j+(k*len_S)][0] < 0 and (agent.issuetree[who][len_DC+k][1] - agent.issuetree[who][len_DC+k][0]) < 0) or (agent.issuetree[who][len_DC+len_PC+len_S+len_DC*len_PC+j+(k*len_S)][0] > 0 and (agent.issuetree[who][len_DC+k][1] - agent.issuetree[who][len_DC+k][0]) > 0):
						S_denominator += abs(agent.issuetree[who][len_DC+len_PC+len_S+len_DC*len_PC+j+(k*len_S)][0]*(agent.issuetree[who][len_DC+k][1] - agent.issuetree[who][len_DC+k][0]))
						# print('This is the PC numerator: ' + str(S_denominator))
					else:
						S_denominator = S_denominator	

		# 1.5.2.2. Addition of the gaps of the associated secondary issues
		for j in range(len_S):
			# Contingency for partial knowledge issues
			if agent.issuetree[who][len_DC+len_PC+j][1] == None or agent.issuetree[who][len_DC+len_PC+j][0] == None:
				S_denominator = S_denominator
			else:
				# print('This is the gap for the PC' + str(i+1) + ': ' + str(agent.issuetree[0][len_DC + len_PC + i][1] - agent.issuetree[0][len_DC + len_PC + i][0]))
				S_denominator += abs(agent.issuetree[who][len_DC+len_PC+j][1] - agent.issuetree[who][len_DC+len_PC+j][0])
		# print('This is the PC denominator: ' + str(S_denominator))
		
		# 1.5.2.3 Calculation the numerator and the preference
		# Select one by one the S
		for j in range(len_S):

			# 1.5.2.3.1. Calculation of the right side of the numerator
			S_numerator = 0
			# print('Selection S' + str(j+1))
			# print('State of the S' + str(j+1) + ': ' + str(agent.issuetree[who][len_DC + len_PC + j][0])) # the state printed
			# Selecting the causal relations starting from PC
			for k in range(len_PC):
				# Contingency for partial knowledge issues
				if agent.issuetree[who][len_DC + k][1] == None or agent.issuetree[who][len_DC + k][0] == None or agent.issuetree[who][len_DC+len_PC+len_S+len_DC*len_PC+j+(k*len_S)][0] == None:
					S_numerator = 0
				else:
					# print('Causal Relation S' + str(j+1) + ' - PC' + str(k+1) + ': ' + str(agent.issuetree[who][len_DC+len_PC+len_S+len_DC*len_PC+j+(k*len_S)][0]))
					# print('Gap of PC' + str(k+1) + ': ' + str((agent.issuetree[who][len_DC + k][1] - agent.issuetree[who][len_DC + k][0])))
					# Check if causal relation and gap are both positive of both negative
					if (agent.issuetree[who][len_DC+len_PC+len_S+len_DC*len_PC+j+(k*len_S)][0] < 0 and (agent.issuetree[who][len_DC+k][1] - agent.issuetree[who][len_DC+k][0]) < 0) or (agent.issuetree[who][len_DC+len_PC+len_S+len_DC*len_PC+j+(k*len_S)][0] > 0 and (agent.issuetree[who][len_DC+k][1] - agent.issuetree[who][len_DC+k][0]) > 0):
						S_numerator += abs(agent.issuetree[who][len_DC+len_PC+len_S+len_DC*len_PC+j+(k*len_S)][0]*(agent.issuetree[who][len_DC+k][1] - agent.issuetree[who][len_DC+k][0]))
						# print('This is the PC numerator: ' + str(S_numerator))
					else:
						S_numerator = S_numerator

			# 1.5.2.3.2. Addition of the gap to the numerator
			# Contingency for partial knowledge issues
			if agent.issuetree[who][len_DC+len_PC+j][1] == None or agent.issuetree[who][len_DC+len_PC+j][0] == None:
				S_numerator += 0
			else:
				# print('This is the gap for the PC' + str(j+1) + ': ' + str(agent.issuetree[who][len_DC+len_PC+j][1] - agent.issuetree[who][len_DC+len_PC+j][0]))
				S_numerator += abs(agent.issuetree[who][len_DC+len_PC+j][1] - agent.issuetree[who][len_DC+len_PC+j][0])
			# print('The numerator is equal to: ' + str(S_numerator))
			# print('The denominator is equal to: ' + str(S_denominator))

			# 1.5.2.3.3. Calculation of the preference
			if S_denominator != 0:
				agent.issuetree[who][len_DC+len_PC+j][2] = round(S_numerator/S_denominator,3) 
			# print('The new preference of the policy core PC' + str(j+1) + ' is: ' + str(agent.issuetree[0][len_DC+j][2]))
			else:
				agent.issuetree[who][len_DC+len_PC+j][2] = 0
Ejemplo n.º 10
0
class Themepark(Model):
    def __init__(self, N_attr, N_cust, width, height, strategy, theme,
                 max_time, weight, adaptive):
        """
        Args:
            N_attr (int): the amount of attractions in the theme park
            N_cust (int): the amout of customers in the theme park
            width (int): the width of the theme park grid
            height (int): the height of the theme park grid
            strategy (str): the strategy of this run (random agents, adaptive agents
                            or adaptive agents with noise)
            theme (str): the setup of the park (circle, random or clustered)
            max_time (int): the number of time steps the park will do in one run
            weight (float): if the customer agents are non-adaptive, this is the strategy
                            they are using
            adaptive (bool): whether customer agents are able to switch strategies
        """
        self.theme = theme
        self.max_time = max_time
        self.N_attr = N_attr
        self.penalty_per = PENALTY_PERCENTAGE
        self.weight = weight
        self.adaptive = adaptive
        self.strategies = STRATEGIES
        self.happinesses = []
        self.N_attr = N_attr
        self.N_cust = N_cust
        self.width = width
        self.height = height
        self.total_steps = 0
        self.cust_ids = N_cust
        self.strategy = strategy
        self.grid = MultiGrid(width, height, torus=False)
        self.schedule = BaseScheduler(self)
        self.schedule_Attraction = BaseScheduler(self)
        self.schedule_Customer = BaseScheduler(self)

        # Cluster or circle coordinates
        if self.theme == "cluster":
            self.x_list, self.y_list, self.positions = xlist, ylist, positions
        elif self.theme == "circle":
            self.x_list, self.y_list, self.positions = get_attraction_coordinates(
                width, height, N_attr, "circle")

        # keeps up the current time
        self.totalTOTAL = 0

        # add attractions to park
        self.attractions = self.make_attractions()
        self.attraction_history = self.make_attr_hist()
        self.running = True
        self.data_dict = {}

        # keep up a history of the strategies
        self.hist_random_strat = []
        self.hist_close_strat = []

        # keep up a history of the occupation of the rides
        self.all_rides_list = []

        # distribution of strategies throughout population
        self.strategy_composition = self.make_strategy_composition()

        # add customers to park
        self.customers = self.add_customers(self.N_cust)

        # keep up park score throughout time
        self.park_score = []

        for attraction in self.get_attractions():
            self.data_dict[attraction.unique_id] = ({
                "id": attraction.unique_id,
                "length": attraction.attraction_duration,
                "waiting_list": []
            })

        # datacollector for the visualisation of the data
        if self.strategy == "Random_test_4":
            self.datacollector = DataCollector({
                "Random":
                lambda m: self.strategy_counter(self.strategies[0]),
                "0.00":
                lambda m: self.strategy_counter(self.strategies[1]),
                "0.25":
                lambda m: self.strategy_counter(self.strategies[2]),
                "0.50":
                lambda m: self.strategy_counter(self.strategies[3]),
                "0.75":
                lambda m: self.strategy_counter(self.strategies[4]),
                "1.00":
                lambda m: self.strategy_counter(self.strategies[5]),
            })

        elif self.strategy == "Random":
            self.datacollector = DataCollector(
                {"Random": lambda m: self.N_cust})

        else:
            self.datacollector = DataCollector({
                "0.00":
                lambda m: self.strategy_counter(self.strategies[0]),
                "0.25":
                lambda m: self.strategy_counter(self.strategies[1]),
                "0.50":
                lambda m: self.strategy_counter(self.strategies[2]),
                "0.75":
                lambda m: self.strategy_counter(self.strategies[3]),
                "1.00":
                lambda m: self.strategy_counter(self.strategies[4]),
            })

        self.datacollector2 = DataCollector(
            {"score": lambda m: self.make_score()})

    def make_score(self):
        """
        Calculates and returns an efficiency score for the enire theme park.
        """

        # calculates the ideal distribution of customers over the attractions per attraction
        ideal = {}
        cust_in_row = 0
        for i in range(len(self.get_attractions())):
            ideal[i] = self.N_cust / self.N_attr
            cust_in_row += self.get_attractions()[i].N_current_cust

        # calculates the difference between the ideal and the real situation
        tot_difference = 0
        for i in range(len(self.get_attractions())):

            difference = abs(cust_in_row / self.N_attr -
                             self.get_attractions()[i].N_current_cust)
            tot_difference += difference

        # the fraction of customers that is not optimally distributed
        fraction_not_right = (tot_difference / self.N_cust)

        # add a penalty by multiplying by the fraction of people currently in a line
        score = abs(1 - (fraction_not_right)) * cust_in_row / self.N_cust
        return score

    def make_attr_hist(self):
        """
        Make history of attractions that are visited for each customer.
        """
        attraction_history = {}
        for attraction in self.get_attractions():

            attraction_history[attraction] = [0] * (self.max_time + 1)
        return attraction_history

    def strategy_counter(self, strategy):
        """
        Input is a strategy, for example "0.25" or "Random", output is
        the number of strategies are adopted at a current time step.
        """
        for attraction_pos in self.positions:
            counter = 0
            for agent in self.customers:
                if agent.weight == strategy:
                    counter += 1
        return counter

    def make_strategy_composition(self):
        """
        Make a composition of all strategies over the entire theme park.
        Returns a dictionary with strategies as keys and the fraction of people
        using this strategy as value.
        """

        # if this is a run with adaptive agents and noise
        if self.strategy == "Random_test_4":
            self.strategies = ["Random_test_4", 0.0, 0.25, 0.50, 0.75, 1.0]

            # make dictionary with fractions (values can be ignored!)
            dict = {
                self.strategies[0]: 0,
                self.strategies[1]: 0.20,
                self.strategies[2]: 0.20,
                self.strategies[3]: 0.20,
                self.strategies[4]: 0.20,
                self.strategies[5]: 0.20
            }

            composition_list = []
            for i in range(len(self.strategies)):
                if i == 0:
                    dict[self.strategies[i]] = FRACTION_RANDOM
                    continue
                else:

                    # choose a random number
                    composition_list.append(random.randint(0, 100))

            sum_comp = sum(composition_list)
            sum_comp = sum_comp - sum_comp * FRACTION_RANDOM
            for i in range(len(self.strategies)):
                if i == 0:
                    continue
                else:

                    # determine the fraction of customer agents with this strategy
                    dict[self.strategies[i]] = composition_list[i -
                                                                1] / sum_comp

        # runs without noise
        else:
            # make dictionary with fractions (values can be ignored!)
            dict = {
                self.strategies[0]: 0.20,
                self.strategies[1]: 0.20,
                self.strategies[2]: 0.20,
                self.strategies[3]: 0.20,
                self.strategies[4]: 0.20
            }

            composition_list = []
            for i in range(len(self.strategies)):

                # choose a random number
                composition_list.append(random.randint(0, 100))

            sum_comp = sum(composition_list)

            for i in range(len(self.strategies)):

                # determine the fraction of agents with this strategy
                dict[self.strategies[i]] = composition_list[i - 1] / sum_comp

        return dict

    def make_attractions(self):
        """
        Initialize attractions on fixed positions, defined in the global
        variables x_list and y_list. Returns a dictionary of attractions
        """

        attractions = {}
        for i in range(self.N_attr):

            pos = (self.x_list[i], self.y_list[i])

            # place attraction if grid cell is empty
            if self.grid.is_cell_empty(pos):

                name = str(i)
                a = Attraction(i, self, pos, name)
                attractions[i] = a

                self.schedule_Attraction.add(a)
                self.grid.place_agent(a, pos)
        return attractions

    def get_attractions(self):
        """
        Return a list with all attractions.
        """
        agents = self.grid.get_neighbors(mid_point,
                                         moore=True,
                                         radius=RADIUS,
                                         include_center=True)

        attractions = []
        for agent in agents:
            if type(agent) == Attraction:
                attractions.append(agent)

        return attractions

    def add_customers(self, N_cust, added=False):
        """
        Initialize customers on random positions.
        Returns a list of all customers
        """

        # a list of weights of which the indices correspond to the id of the agent
        # to who this weight is given
        weights_list = []

        # Adaptive strategy
        if self.adaptive is True:

            # Decide weights for every customer
            for j in self.strategy_composition.keys():
                for i in range(round(N_cust * self.strategy_composition[j])):
                    weights_list.append(j)

            # Add weights for the random strategy
            if len(weights_list) < self.N_cust:
                rand = random.choice(self.strategies)
                weights_list.append(rand)
            elif len(weights_list) > self.N_cust:
                rand = random.choice(weights_list)
                weights_list.remove(rand)

        else:
            if self.strategy is not "Random":
                for i in range(round(N_cust)):
                    print(self.weight)
                    weights_list.append(self.weight)

        cust_list = []
        for i in range(N_cust):

            # Get random location within grid height and width
            pos_temp = [
                random.randint(0, WIDTH - 1),
                random.randint(0, HEIGHT - 1)
            ]
            rand_x, rand_y = pos_temp[0], pos_temp[1]
            pos = (rand_x, rand_y)

            if added is True:
                i = self.cust_ids
            if self.strategy == "Random_test_4":
                if weights_list[i] == "Random_test_4":
                    strategy = "Random_test_4"
                else:
                    strategy = "Closest_by"
            else:
                strategy = self.strategy

            if weights_list == []:
                weight = None
            else:
                weight = weights_list[i]

            # make customer
            a = Customer(i, self, pos, strategy, weight, self.adaptive)
            self.schedule_Customer.add(a)
            self.grid.place_agent(a, pos)
            cust_list.append(a)

        return cust_list

    def calculate_people(self):
        """
        Calculate how many customers are in which attraction.
        Returns a list of which the indices correspond to the ids of the atttraction
        """

        counter_total = {}

        # loop through attractions
        for attraction_pos in self.positions:

            agents = self.grid.get_neighbors(attraction_pos,
                                             moore=True,
                                             radius=0,
                                             include_center=True)

            counter = 0

            # find customers in this attraction
            for agent in agents:
                if type(agent) is Customer:
                    counter += 1
                else:
                    attraction = agent

            # update the amount of customers in this attraction
            attraction.N_current_cust = counter
            counter_total[attraction.unique_id] = counter

        return list(counter_total.values())

    def calc_waiting_time(self):
        """
        Return a dictionary of watingtimes for every attraction
        """
        counter_total = {}

        attractions = self.get_attractions()
        for attraction in attractions:

            counter_total[
                attraction.unique_id] = attraction.current_waitingtime

        return counter_total

    def calculate_people_sorted(self):
        """
        Calculate how many customers are in which attraction.
        Returns a dictionary with attraction-ids as keys and customers in line as values.
        """

        counter_total = {}

        # loop through attractions
        for attraction_pos in self.positions:

            agents = self.grid.get_neighbors(attraction_pos,
                                             moore=True,
                                             radius=0,
                                             include_center=True)

            counter = 0

            # find customers in this attraction
            for agent in agents:
                if type(agent) is Customer:
                    counter += 1
                else:
                    attraction = agent

            # update the amount of customers in this attraction
            attraction.N_current_cust = counter
            self.attraction_history[attraction][self.totalTOTAL] = counter
            counter_total[attraction.unique_id] = counter

        return counter_total

    def get_strategy_history(self):
        """ Update history with how many customers chose which strategy """

        customers = self.get_customers()
        randomstrat, closebystrat = 0, 0

        for customer in customers:
            if customer.strategy == "Random" or customer.strategy == "Random_test_4":
                randomstrat += 1
            elif customer.strategy == "Closest_by":
                closebystrat += 1

        self.hist_random_strat.append(randomstrat)
        self.hist_close_strat.append(closebystrat)

    def get_customers(self):
        """Return a list of all customers in the theme park."""
        agents = self.grid.get_neighbors(mid_point,
                                         moore=True,
                                         radius=RADIUS,
                                         include_center=True)

        customers = []

        # Count customer agents
        for agent in agents:
            if type(agent) == Customer:
                customers.append(agent)
        return customers

    def get_data_customers(self):
        """ Return dictionary with data of customers """

        data = {}
        agents = self.grid.get_neighbors(mid_point,
                                         moore=True,
                                         radius=RADIUS,
                                         include_center=True)

        for agent in agents:
            if type(agent) is Customer:
                data[agent.unique_id] = {
                    "totalwaited": agent.total_ever_waited,
                    "visited_attractions": agent.nmbr_attractions,
                    "strategy": agent.strategy,
                    "swapped_strat": agent.strategy_swap_hist
                }
        return data

    def calc_hapiness(self):
        """
        Calculate mean hapiness of all customers, based on:

        - How many rides were taken
        - Number of times in the same attraction
        - Total waiting time

        Returns the mean happiness of all customers
        """
        customers = self.get_customers()
        scores = []

        for customer in customers:
            history = customer.history
            values = list(history.values())
            total_rides = sum(values)

            if total_rides != 0:
                scores.append(total_rides / self.N_attr -
                              self.totalTOTAL / customer.total_ever_waited)
            else:
                return None

        scores = np.interp(scores, (min(scores), max(scores)), (1, 10))
        return np.mean(scores)

    def get_history_list(self):
        """
        Return a dictionary of the history of visited attraction for
        each customer.
        """

        customers = self.get_customers()
        histories = {}

        for customer in customers:
            history = customer.history
            values = list(history.values())
            histories[customer.unique_id] = values
        return histories

    def final(self):
        """ Return and save data at the end of the run."""

        hist_list = []
        agents = self.grid.get_neighbors(mid_point,
                                         moore=True,
                                         radius=RADIUS,
                                         include_center=True)
        attractions = self.get_attractions()

        # Make a list for the fraction of occupied rides at all time steps
        self.all_rides_list = [0] * len(attractions[0].in_attraction_list)
        for attraction in attractions:
            for i in range(len(attraction.in_attraction_list)):
                self.all_rides_list[i] += attraction.in_attraction_list[i]
        for i in range(len(self.all_rides_list)):
            self.all_rides_list[i] /= self.N_attr

        cust_data = self.get_data_customers()
        for agent in agents:
            if type(agent) is Customer:
                sum_attr = sum(agent.history.values())
                if sum_attr > 0:
                    hist_list.append(agent.strategy_swap_hist)
                else:
                    hist_list.append(agent.strategy_swap_hist)

        histories = self.get_history_list()

        # save data
        try:
            pickle.dump(self.datacollector.get_model_vars_dataframe(),
                        open("../data/strategy_history.p", 'wb'))
            pickle.dump(self.datacollector2.get_model_vars_dataframe(),
                        open("../data/eff_score_history.p", 'wb'))
            pickle.dump(cust_data, open("../data/customers.p", 'wb'))
            pickle.dump(self.park_score[-1], open("../data/park_score.p",
                                                  "wb"))
            pickle.dump(self.happinesses, open("../data/hapiness.p", "wb"))
            pickle.dump(histories, open("../data/cust_history.p", 'wb'))
            pickle.dump(self.all_rides_list, open("../data/all_rides.p", "wb"))
        except:
            pickle.dump(self.datacollector.get_model_vars_dataframe(),
                        open("data/strategy_history.p", 'wb'))
            pickle.dump(self.datacollector2.get_model_vars_dataframe(),
                        open("data/eff_score_history.p", 'wb'))
            pickle.dump(cust_data, open("data/customers.p", 'wb'))
            pickle.dump(self.park_score[-1], open("data/park_score.p", "wb"))
            pickle.dump(self.happinesses, open("data/hapiness.p", "wb"))
            pickle.dump(histories, open("data/cust_history.p", 'wb'))
            pickle.dump(self.all_rides_list, open("data/all_rides.p", "wb"))

        print()
        print("RUN HAS ENDED")
        print()

    def save_data(self):
        """Save data of all attractions and customers."""

        # Get info
        waitinglines = self.calc_waiting_time()

        for i in range(len(self.attractions)):
            self.data_dict[i]["waiting_list"].append(waitinglines.get(i))

        self.park_score.append(sum(waitinglines.values()))
        self.happinesses.append(self.calc_hapiness())

    def step(self):
        """Advance the model by one step."""
        if self.totalTOTAL < self.max_time:
            self.totalTOTAL += 1
            self.schedule.step()

            # Collect data for every step
            self.datacollector.collect(self)
            self.datacollector2.collect(self)

            # Step for both attraction and customer
            self.schedule_Attraction.step()
            self.schedule_Customer.step()

            self.total_steps += 1

            # Save data and update strategy history
            self.save_data()
            self.get_strategy_history()

        else:
            for key in self.attraction_history.keys():
                y = self.attraction_history[key]
                x = list(range(0, self.max_time))
            self.final()
Ejemplo n.º 11
0
class FireEvacuation(Model):
    MIN_HEALTH = 0.75
    MAX_HEALTH = 1

    MIN_SPEED = 1
    MAX_SPEED = 2

    MIN_NERVOUSNESS = 1
    MAX_NERVOUSNESS = 10

    MIN_EXPERIENCE = 1
    MAX_EXPERIENCE = 10

    MIN_VISION = 1
    # MAX_VISION is simply the size of the grid

    def __init__(self, floor_plan_file, human_count, collaboration_percentage, fire_probability, visualise_vision, random_spawn, save_plots):
        # Load floorplan
        # floorplan = np.genfromtxt(path.join("fire_evacuation/floorplans/", floor_plan_file))
        with open(os.path.join("fire_evacuation/floorplans/", floor_plan_file), "rt") as f:
            floorplan = np.matrix([line.strip().split() for line in f.readlines()])

        # Rotate the floorplan so it's interpreted as seen in the text file
        floorplan = np.rot90(floorplan, 3)

        # Check what dimension our floorplan is
        width, height = np.shape(floorplan)

        # Init params
        self.width = width
        self.height = height
        self.human_count = human_count
        self.collaboration_percentage = collaboration_percentage
        self.visualise_vision = visualise_vision
        self.fire_probability = fire_probability
        self.fire_started = False  # Turns to true when a fire has started
        self.save_plots = save_plots

        # Set up model objects
        self.schedule = RandomActivation(self)

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

        # Used to start a fire at a random furniture location
        self.furniture_list = []

        # Used to easily see if a location is a FireExit or Door, since this needs to be done a lot
        self.fire_exit_list = []
        self.door_list = []

        # If random spawn is false, spawn_list will contain the list of possible spawn points according to the floorplan
        self.random_spawn = random_spawn
        self.spawn_list = []

        # Load floorplan objects
        for (x, y), value in np.ndenumerate(floorplan):
            value = str(value)
            floor_object = None
            if value is "W":
                floor_object = Wall((x, y), self)
            elif value is "E":
                floor_object = FireExit((x, y), self)
                self.fire_exit_list.append((x, y))
                self.door_list.append((x, y))  # Add fire exits to doors as well, since, well, they are
            elif value is "F":
                floor_object = Furniture((x, y), self)
                self.furniture_list.append((x, y))
            elif value is "D":
                floor_object = Door((x, y), self)
                self.door_list.append((x, y))
            elif value is "S":
                self.spawn_list.append((x, y))

            if floor_object:
                self.grid.place_agent(floor_object, (x, y))
                self.schedule.add(floor_object)

        # Create a graph of traversable routes, used by agents for pathing
        self.graph = nx.Graph()
        for agents, x, y in self.grid.coord_iter():
            pos = (x, y)

            # If the location is empty, or a door
            if not agents or any(isinstance(agent, Door) for agent in agents):
                neighbors = self.grid.get_neighborhood(pos, moore=True, include_center=True, radius=1)

                for neighbor in neighbors:
                    # If there is contents at this location and they are not Doors or FireExits, skip them
                    if not self.grid.is_cell_empty(neighbor) and neighbor not in self.door_list:
                        continue

                    self.graph.add_edge(pos, neighbor)

        # Collects statistics from our model run
        self.datacollector = DataCollector(
            {
                "Alive": lambda m: self.count_human_status(m, Human.Status.ALIVE),
                "Dead": lambda m: self.count_human_status(m, Human.Status.DEAD),
                "Escaped": lambda m: self.count_human_status(m, Human.Status.ESCAPED),
                "Incapacitated": lambda m: self.count_human_mobility(m, Human.Mobility.INCAPACITATED),
                "Normal": lambda m: self.count_human_mobility(m, Human.Mobility.NORMAL),
                "Panic": lambda m: self.count_human_mobility(m, Human.Mobility.PANIC),
                "Verbal Collaboration": lambda m: self.count_human_collaboration(m, Human.Action.VERBAL_SUPPORT),
                "Physical Collaboration": lambda m: self.count_human_collaboration(m, Human.Action.PHYSICAL_SUPPORT),
                "Morale Collaboration": lambda m: self.count_human_collaboration(m, Human.Action.MORALE_SUPPORT)
            }
        )

        # Calculate how many agents will be collaborators
        number_collaborators = int(round(self.human_count * (self.collaboration_percentage / 100)))

        # Start placing human agents
        for i in range(0, self.human_count):
            if self.random_spawn:  # Place human agents randomly
                pos = self.grid.find_empty()
            else:  # Place human agents at specified spawn locations
                pos = random.choice(self.spawn_list)

            if pos:
                # Create a random human
                health = random.randint(self.MIN_HEALTH * 100, self.MAX_HEALTH * 100) / 100
                speed = random.randint(self.MIN_SPEED, self.MAX_SPEED)

                if number_collaborators > 0:
                    collaborates = True
                    number_collaborators -= 1
                else:
                    collaborates = False

                # Vision statistics obtained from http://www.who.int/blindness/GLOBALDATAFINALforweb.pdf
                vision_distribution = [0.0058, 0.0365, 0.0424, 0.9153]
                vision = int(np.random.choice(np.arange(self.MIN_VISION, self.width + 1, (self.width / len(vision_distribution))), p=vision_distribution))

                nervousness_distribution = [0.025, 0.025, 0.1, 0.1, 0.1, 0.3, 0.2, 0.1, 0.025, 0.025]  # Distribution with slight higher weighting for above median nerovusness
                nervousness = int(np.random.choice(range(self.MIN_NERVOUSNESS, self.MAX_NERVOUSNESS + 1), p=nervousness_distribution))  # Random choice starting at 1 and up to and including 10

                experience = random.randint(self.MIN_EXPERIENCE, self.MAX_EXPERIENCE)

                belief_distribution = [0.9, 0.1]  # [Believes, Doesn't Believe]
                believes_alarm = np.random.choice([True, False], p=belief_distribution)

                human = Human(pos, health=health, speed=speed, vision=vision, collaborates=collaborates, nervousness=nervousness, experience=experience, believes_alarm=believes_alarm, model=self)

                self.grid.place_agent(human, pos)
                self.schedule.add(human)
            else:
                print("No tile empty for human placement!")

        self.running = True

    # Plots line charts of various statistics from a run
    def save_figures(self):
        DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
        OUTPUT_DIR = DIR + "/output"

        results = self.datacollector.get_model_vars_dataframe()

        dpi = 100
        fig, axes = plt.subplots(figsize=(1920 / dpi, 1080 / dpi), dpi=dpi, nrows=1, ncols=3)

        status_results = results.loc[:, ['Alive', 'Dead', 'Escaped']]
        status_plot = status_results.plot(ax=axes[0])
        status_plot.set_title("Human Status")
        status_plot.set_xlabel("Simulation Step")
        status_plot.set_ylabel("Count")

        mobility_results = results.loc[:, ['Incapacitated', 'Normal', 'Panic']]
        mobility_plot = mobility_results.plot(ax=axes[1])
        mobility_plot.set_title("Human Mobility")
        mobility_plot.set_xlabel("Simulation Step")
        mobility_plot.set_ylabel("Count")

        collaboration_results = results.loc[:, ['Verbal Collaboration', 'Physical Collaboration', 'Morale Collaboration']]
        collaboration_plot = collaboration_results.plot(ax=axes[2])
        collaboration_plot.set_title("Human Collaboration")
        collaboration_plot.set_xlabel("Simulation Step")
        collaboration_plot.set_ylabel("Successful Attempts")
        collaboration_plot.set_ylim(ymin=0)

        timestr = time.strftime("%Y%m%d-%H%M%S")
        plt.suptitle("Percentage Collaborating: " + str(self.collaboration_percentage) + "%, Number of Human Agents: " + str(self.human_count), fontsize=16)
        plt.savefig(OUTPUT_DIR + "/model_graphs/" + timestr + ".png")
        plt.close(fig)

    # Starts a fire at a random piece of furniture with file_probability chance
    def start_fire(self):
        rand = random.random()
        if rand < self.fire_probability:
            fire_furniture = random.choice(self.furniture_list)
            fire = Fire(fire_furniture, self)
            self.grid.place_agent(fire, fire_furniture)
            self.schedule.add(fire)
            self.fire_started = True
            print("Fire started at:", fire_furniture)

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

        self.schedule.step()

        # If there's no fire yet, attempt to start one
        if not self.fire_started:
            self.start_fire()

        self.datacollector.collect(self)

        # If no more agents are alive, stop the model and collect the results
        if self.count_human_status(self, Human.Status.ALIVE) == 0:
            self.running = False

            if self.save_plots:
                self.save_figures()

    @staticmethod
    def count_human_collaboration(model, collaboration_type):
        """
        Helper method to count the number of collaborations performed by Human agents in the model
        """

        count = 0
        for agent in model.schedule.agents:
            if isinstance(agent, Human):
                if collaboration_type == Human.Action.VERBAL_SUPPORT:
                    count += agent.get_verbal_collaboration_count()
                elif collaboration_type == Human.Action.MORALE_SUPPORT:
                    count += agent.get_morale_collaboration_count()
                elif collaboration_type == Human.Action.PHYSICAL_SUPPORT:
                    count += agent.get_physical_collaboration_count()

        return count

    @staticmethod
    def count_human_status(model, status):
        """
        Helper method to count the status of Human agents in the model
        """
        count = 0
        for agent in model.schedule.agents:
            if isinstance(agent, Human):
                if agent.get_status() == status:
                    count += 1
        return count

    @staticmethod
    def count_human_mobility(model, mobility):
        """
        Helper method to count the mobility of Human agents in the model
        """
        count = 0
        for agent in model.schedule.agents:
            if isinstance(agent, Human):
                if agent.get_mobility() == mobility:
                    count += 1
        return count
Ejemplo n.º 12
0
class Simple_Language_Model(Model):
    def __init__(self, num_people, width=5, height=5, max_people_factor=5,
                 init_lang_distrib=[0.25, 0.65, 0.1], num_cities=10, lang_ags_sorted_by_dist=True,
                 lang_ags_sorted_in_clust=True):
        self.num_people = num_people
        self.grid_width = width
        self.grid_height = height
        self.max_people_factor = max_people_factor
        self.init_lang_distrib = init_lang_distrib
        self.num_cities = num_cities
        self.lang_ags_sorted_by_dist = lang_ags_sorted_by_dist
        self.lang_ags_sorted_in_clust = lang_ags_sorted_in_clust
        self.clust_centers = None
        self.cluster_sizes = None

        # define grid and schedule
        self.grid = MultiGrid(height, width, False)
        self.schedule = RandomActivation(self)

        ## RANDOMLY DEFINE ALL CITY-CENTERS COORDS (CITY == HOMES, JOB CENTERS and SCHOOLS)
        # first define available points as pct of squared grid length
        grid_pct_list = np.linspace(0.1, 0.9, 100) # avoid edges
        # now generate the cluster centers (CITIES-VILLAGES)
        self.clust_centers = np.random.choice(grid_pct_list,size=(self.num_cities, 2),replace=False)
        if lang_ags_sorted_by_dist:
            self.clust_centers = sorted(self.clust_centers,
                                        key=lambda x:pdist([x,[0,0]])
                                       )



        # INITIALIZE KNOWN PEOPLE NETWORK => label is lang spoken
        self.known_people_network = nx.DiGraph()
        #        self.known_people_network.add_edge('A','B', lang_spoken = 'cat')
        #        self.known_people_network.add_edge('A','C', lang_spoken = 'spa')
        #        self.known_people_network['A']['C']['lang_spoken']
        #        self.known_people_network['B']['A'] = 'cat'

        # INITIALIZE FRIENDSHIP NETWORK
        self.friendship_network = nx.Graph()  # sort by friendship intensity
        #       sorted(self.friendship_network[n_1].items(),
        #                    key=lambda edge: edge[1]['link_strength'],
        #                    reverse = True)

        # INITIALIZE FAMILY NETWORK
        self.family_network = nx.DiGraph()

        # ADD ALL AGENTS TO GRID AND SCHEDULE
        S = 0.5
        if (not lang_ags_sorted_by_dist) and (not lang_ags_sorted_in_clust):
            for id_ in range(self.num_people):
                x = random.randrange(self.grid_width)
                y = random.randrange(self.grid_height)
                coord = (x,y)
                lang = np.random.choice([0,1,2], p=self.init_lang_distrib)
                ag = Simple_Language_Agent(self, id_, lang, S)
                self.add_agent(ag, coord)
        else:
            self.create_lang_agents()

        # DATA COLLECTOR
        self.datacollector = DataCollector(
            model_reporters={"count_spa": lambda m: m.get_lang_stats(0),
                             "count_bil": lambda m: m.get_lang_stats(1),
                             "count_cat": lambda m: m.get_lang_stats(2),
                             "total_num_agents": lambda m:len(m.schedule.agents),
                             "biling_evol_h": lambda m:m.get_bilingual_global_evol('heard'),
                             "biling_evol_s": lambda m: m.get_bilingual_global_evol('spoken')}
        )

    def add_agent(self, a, coords):
        """Method to add a given agent to grid, schedule and system networks

        Arguments:
            * a : agent class instance
            * coords : agent location on grid (2-D tuple of integers)

        """
        # add agent to grid and schedule
        self.schedule.add(a)
        self.grid.place_agent(a, (coords[0], coords[1]))
        ## add agent node to all networks
        self.known_people_network.add_node(a)
        self.friendship_network.add_node(a)
        self.family_network.add_node(a)

    def compute_cluster_sizes(self, min_size=20, small_large_pcts=[0.6, 0.4]):
        """ Method to compute sizes of each agent cluster

        Arguments:
            * min_size: minimum accepted cluster size ( integer)
            * small_large_pcts: percentages of small and large cities over total ( list of floats  0<x<1)

        Returns:
            * list of integers representing cluster sizes

        """
        if min_size * self.num_cities >= self.num_people:
            raise ValueError('num_people should be greater than min_size * num_cities ')
        size_choices = [max(int(self.num_people / (10 * self.num_cities)), min_size),
                        max(int(self.num_people / self.num_cities), min_size)]
        city_sizes = np.random.choice(size_choices, p=small_large_pcts, size=self.num_cities - 1)
        last_city_size = self.num_people - city_sizes.sum()
        city_sizes = np.append(city_sizes, last_city_size)
        pcts = np.random.dirichlet(city_sizes)
        return np.random.multinomial(city_sizes.sum(), pcts)

    def generate_cluster_points_coords(self, pct_grid_w, pct_grid_h, clust_size):
        """ Using binomial ditribution, this method generates initial coordinates
            for a given cluster, defined via its center and its size.
            Cluster size as well as cluster center coords
            (in grid percentage) must be provided

        Arguments:
            * pct_grid_w: positive float < 1 to define clust_center along grid width
            * pct_grid_h: positive float < 1 to define clust_center along grid height
            * clust_size: desired size of the cluster being generated

        Returns:
            * cluster_coordinates: two numpy arrays with x and y coordinates
            respectively

        """
        ## use binomial generator to get clusters in width * height grid
        ## n = grid_width, p = pct_grid, size = num_experim
        x_coords = np.random.binomial(self.grid_width,
                                      pct_grid_w,
                                      size=clust_size)
        for idx, elem in enumerate(x_coords):
            if elem >= self.grid_width:
                x_coords[idx] = self.grid_width - 1

        y_coords = np.random.binomial(self.grid_height,
                                      pct_grid_h,
                                      size=clust_size)
        for idx, elem in enumerate(y_coords):
            if elem >= self.grid_width:
                y_coords[idx] = self.grid_height - 1
        return x_coords, y_coords

    def create_lang_agents(self):
        """ Method to instantiate all agents

        Arguments:
            * sort_lang_types_by_dist: boolean to specify
                if agents must be sorted by distance to global origin
            * sort_sub_types_within_clust: boolean to specify
                if agents must be sorted by distance to center of cluster they belong to

            """

        self.cluster_sizes = self.compute_cluster_sizes()
        array_langs = np.random.choice([0, 1, 2], p=self.init_lang_distrib, size=self.num_people)
        if self.lang_ags_sorted_by_dist:
            array_langs.sort()
        idxs_to_split = self.cluster_sizes.cumsum()
        langs_per_clust = np.split(array_langs, idxs_to_split)
        if (not self.lang_ags_sorted_by_dist) and (self.lang_ags_sorted_in_clust):
            for subarray in langs_per_clust:
                subarray.sort()  # invert if needed
        ids = set(range(self.num_people))

        for clust_idx, (clust_size, clust_c_coords) in enumerate(zip(self.cluster_sizes, self.clust_centers)):
            x_cs, y_cs = self.generate_cluster_points_coords(clust_c_coords[0], clust_c_coords[1], clust_size)
            if (not self.lang_ags_sorted_by_dist) and (self.lang_ags_sorted_in_clust):
                clust_p_coords = sorted(list(zip(x_cs, y_cs)),
                                        key=lambda x:pdist([x, [self.grid_width*self.clust_centers[clust_idx][0],
                                                                self.grid_height*self.clust_centers[clust_idx][1]]
                                                            ])
                                        )
                x_cs, y_cs = list(zip(*clust_p_coords))
            for ag_lang, x, y in zip(langs_per_clust[clust_idx], x_cs, y_cs):
                ag = Simple_Language_Agent(self, ids.pop(), ag_lang, 0.5)
                self.add_agent(ag, (x, y))


    def get_lang_stats(self, i):
        """Method to get counts of each type of lang agent

        Arguments:
            * i : integer from [0,1,2] hat specifies agent lang type

        Returns:
            * lang type count as percentage of total

        """
        ag_lang_list = [ag.language for ag in self.schedule.agents]
        num_ag = len(ag_lang_list)
        lang_counts = Counter(ag_lang_list)
        return lang_counts[i]/num_ag

    def get_bilingual_global_evol(self, lang_typology):
        """Method to compute internal linguistic structure of all bilinguals,
        expressed as average amount of Catalan heard or spoken as % of total

         Arguments:
             * lang_typology: string that can take either of two values 'heard' or 'spoken'

         Returns:
             * float representing the AVERAGE percentage of Catalan in bilinguals

        """
        list_biling = [(ag.lang_freq['cat_pct_h'], ag.lang_freq['cat_pct_s'])
                       for ag in self.schedule.agents if ag.language == 1]
        if lang_typology == 'heard':
            if list_biling:
                return np.array(list(zip(*list_biling))[0]).mean()
            else:
                if self.get_lang_stats(2) > self.get_lang_stats(0):
                    return 1
                else:
                    return 0
        else:
            if list_biling:
                return np.array(list(zip(*list_biling))[1]).mean()
            else:
                if self.get_lang_stats(2) > self.get_lang_stats(0):
                    return 1
                else:
                    return 0

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

    def run_model(self, steps, save_frames_freq=0):
        pbar = pyprind.ProgBar(steps)
        for _ in range(steps):
            self.step()
            if save_frames_freq:
                if not self.schedule.steps%save_frames_freq:
                    self.show_results(step=self.schedule.steps, plot_results=False, save_fig=True)
            pbar.update()

    def create_agents_attrs_data(self, ag_attr, plot=False):
        ag_and_coords = [(getattr(ag, ag_attr), ag.pos[0], ag.pos[1])
                         for ag in self.schedule.agents]
        ag_and_coords = np.array(ag_and_coords)
        df_attrs = pd.DataFrame({'values': ag_and_coords[:, 0],
                                 'x': ag_and_coords[:, 1],
                                 'y': ag_and_coords[:, 2]})
        self.df_attrs_avg = df_attrs.groupby(['x', 'y']).mean()

        if plot:
            s = plt.scatter(self.df_attrs_avg.reset_index()['x'],
                            self.df_attrs_avg.reset_index()['y'],
                            c=self.df_attrs_avg.reset_index()['values'],
                            vmin=0, vmax=2, s=30,
                            cmap='viridis')
            plt.colorbar(s)
            plt.show()


    def show_results(self, ag_attr='language', step=None,
                     plot_results=True, plot_type='imshow', save_fig=False):

        grid_size = (3, 5)
        self.create_agents_attrs_data(ag_attr)

        data_2_plot = self.datacollector.get_model_vars_dataframe()[:step]
        data_2D = self.df_attrs_avg.reset_index()

        ax1 = plt.subplot2grid(grid_size, (0, 3), rowspan=1, colspan=2)
        data_2_plot[["count_bil", "count_cat", "count_spa"]].plot(ax=ax1, title='lang_groups')
        ax1.xaxis.tick_bottom()
        ax1.legend(loc='best', prop={'size': 8})
        ax2 = plt.subplot2grid(grid_size, (1, 3), rowspan=1, colspan=2)
        data_2_plot['total_num_agents'].plot(ax=ax2, title='num_agents')
        ax3 = plt.subplot2grid(grid_size, (2, 3), rowspan=1, colspan=2)
        data_2_plot[['biling_evol_h', 'biling_evol_s']].plot(ax=ax3, title='biling_quality')
        ax3.legend(loc='best', prop={'size': 8})
        ax4 = plt.subplot2grid(grid_size, (0, 0), rowspan=3, colspan=3)
        if plot_type == 'imshow':
            s = ax4.imshow(self.df_attrs_avg.unstack('x'), vmin=0, vmax=2, cmap='viridis',
                           interpolation='nearest', origin='lower')
        else:
            s = ax4.scatter(data_2D['x'],
                            data_2D['y'],
                            c=data_2D['values'],
                            vmin=0, vmax=2, s=35,
                            cmap='viridis')
        ax4.text(0.02, 0.95, 'time = %.1f' % self.schedule.steps, transform=ax4.transAxes)
        plt.colorbar(s)
        plt.tight_layout()
        if save_fig:
            plt.savefig('step' + str(step) + '.png')
        if plot_results:
            plt.show()

    def run_and_animate(self, steps, plot_type='imshow'):
        fig = plt.figure()
        grid_size = (3, 5)
        ax1 = plt.subplot2grid(grid_size, (0, 3), rowspan=1, colspan=2)
        ax1.set_xlim(0, steps)
        ax1.set_ylim(0, 1)
        ax1.xaxis.tick_bottom()
        line10, = ax1.plot([], [], lw=2, label='count_spa')
        line11, = ax1.plot([], [], lw=2, label='count_bil')
        line12, = ax1.plot([], [], lw=2, label='count_cat')
        ax1.legend(loc='best', prop={'size': 8})
        ax1.set_title("lang_groups")
        ax2 = plt.subplot2grid(grid_size, (1, 3), rowspan=1, colspan=2)
        ax2.set_xlim(0, steps)
        ax2.set_ylim(0, self.max_people_factor * self.num_people)
        line2, = ax2.plot([], [], lw=2, label = "total_num_agents")
        ax2.legend(loc='best', prop={'size': 8})
        ax2.set_title("num_agents")
        ax3 = plt.subplot2grid(grid_size, (2, 3), rowspan=1, colspan=2)
        ax3.set_xlim(0, steps)
        ax3.set_ylim(0, 1)
        line30, = ax3.plot([], [], lw=2, label='biling_evol_h')
        line31, = ax3.plot([], [], lw=2, label='biling_evol_s')
        ax3.legend(loc='best', prop={'size': 8})
        ax3.set_title("biling_quality")
        ax4 = plt.subplot2grid(grid_size, (0, 0), rowspan=3, colspan=3)
        ax4.set_xlim(0, self.grid_width-1)
        ax4.set_ylim(0, self.grid_height-1)
        if plot_type == 'imshow':
            im_2D = ax4.imshow(np.zeros((self.grid_width, self.grid_height)),
                               vmin=0, vmax=2, cmap='viridis',
                               interpolation='nearest', origin='lower')
            fig.colorbar(im_2D)
        elif plot_type == 'scatter':
            dots = ax4.scatter([], [], c=[], vmin=0, vmax=2, cmap='viridis')
            fig.colorbar(dots)
        time_text = ax4.text(0.02, 0.95, '', transform=ax4.transAxes)

        def init_show():
            if plot_type == 'imshow':
                im_2D.set_array(np.random.choice([np.nan, 0], p=[1, 0], size=(self.grid_width, self.grid_height)))
                return im_2D,
            elif plot_type == 'scatter':
                dots.set_offsets([0,0])
                return dots,

        def run_and_update(i):
            #run model step
            self.step()

            #create plots and data for 1D plots
            data = self.datacollector.get_model_vars_dataframe()
            line10.set_data(data.index, data['count_spa'])
            line11.set_data(data.index, data['count_bil'])
            line12.set_data(data.index, data['count_cat'])

            line2.set_data(data.index, data['total_num_agents'])

            line30.set_data(data.index, data['biling_evol_h'])
            line31.set_data(data.index, data['biling_evol_s'])
            # generate data for 2D representation
            self.create_agents_attrs_data('language')
            # create 2D plot
            time_text.set_text('time = %.1f' % i)
            if plot_type == 'imshow':
                im_2D.set_array(self.df_attrs_avg.unstack('x'))
                return line10, line11, line12, line2, line30, line31, im_2D, time_text
            else:
                data = np.hstack((self.df_attrs_avg.reset_index()['x'][:, np.newaxis],
                                  self.df_attrs_avg.reset_index()['y'][:, np.newaxis]))
                dots.set_offsets(data)
                dots.set_array(self.df_attrs_avg.reset_index()['values'])
                return line10, line11, line12, line2, line30, line31, dots, time_text

        # generate persistent animation object
        ani = animation.FuncAnimation(fig, run_and_update,init_func=init_show,
                                      frames=steps, interval=100, blit=True, repeat=False)
        plt.tight_layout()
        plt.show()

    def save_model_data(self):
        self.model_data = {'initial_conditions':{'cluster_sizes': self.cluster_sizes,
                                                 'cluster_centers': self.clust_centers,
                                                 'init_num_people': self.num_people,
                                                 'grid_width': self.grid_width,
                                                 'grid_height': self.grid_height,
                                                 'init_lang_distrib': self.init_lang_distrib,
                                                 'num_cities': self.num_cities,
                                                 'sort_by_dist': self.lang_ags_sorted_by_dist,
                                                 'sort_within_clust': self.lang_ags_sorted_in_clust},
                           'model_results': self.datacollector.get_model_vars_dataframe()}
        dd.io.save('model_data.h5', self.model_data)

    def load_model_data(self, data_filename, key='/' ):
        return dd.io.load(data_filename,key)







        
Ejemplo n.º 13
0
class CDAmodel(Model):
    """Continuous Double Auction model with some number of agents."""
    def __init__(self,
                 supply,
                 demand,
                 s_strategy,
                 b_strategy,
                 highest_ask=100,
                 lowest_ask=0):
        self.supply = supply
        self.demand = demand
        self.num_sellers = supply.num_agents
        self.num_buyers = demand.num_agents
        self.initialize_spread()
        self.market_price = None
        self.history = Order_history()
        self.num_traded = 0
        self.num_step = 0
        # history records an order as a bid or ask only if it updates
        # the spread

        self.num_period = 1
        self.loc_period = [0]  # where each period happens

        # Sometimes trade does not happen within a period,
        # so we need variables to indicate them.
        self.no_trade = False

        # When a shift happens, I need to know it so that
        # I can calculate efficiency properly.
        self.shifted = False
        self.shifted_period = -1
        self.new_supply = None
        self.new_demand = None

        # How agents are activated at each step
        self.schedule = RandomChoiceActivation(self)
        # Create agents
        for i, cost in enumerate(self.supply.price_schedule):
            self.schedule.add(
                b_strategy(i, self, "seller", cost, supply.q_per_agent,
                           highest_ask, lowest_ask))
        for i, value in enumerate(self.demand.price_schedule):
            j = self.num_sellers + i
            self.schedule.add(
                s_strategy(j, self, "buyer", value, demand.q_per_agent,
                           highest_ask, lowest_ask))

        # Collecting data
        # self.datacollector = DataCollector(
        #     model_reporters={"Period": "num_period",
        #                      "OB": "outstanding_bid",
        #                      "OBer": get_bidder_id,
        #                      "OA": "outstanding_ask",
        #                      "OAer": get_asker_id,
        #                      "MarketPrice": "market_price",
        #                      "Traded": "traded",
        #                      "Order": lambda x: x.history.get_last_action(),
        #                      "Efficiency": compute_efficiency},
        #     agent_reporters={"Period": lambda x: x.model.num_period,
        #                      "Type": lambda x: type(x),
        #                      "Role": "role",
        #                      "Value": "value",
        #                      "Good": "good",
        #                      "Right": "right",
        #                      "Surplus": "surplus"}
        # )
        self.datacollector = DataCollector(model_reporters={
            "Step": "num_step",
            "Period": "num_period",
            "TransNum": "num_traded",
            "OB": "outstanding_bid",
            "OA": "outstanding_ask",
            "MarketPrice": "market_price",
            "Traded": "traded",
            "CumulativeActualSurplus": compute_actual_surplus,
            "TheoreticalSurplus": compute_theoretical_surplus,
            "CumulativeTS": compute_acc_theoretical_surplus,
            "Efficiency": compute_efficiency
        },
                                           agent_reporters={
                                               "Period":
                                               lambda x: x.model.num_period,
                                               "Type": lambda x: type(x),
                                               "Role": "role",
                                               "Value": "value",
                                               "Good": "good",
                                               "Right": "right",
                                               "Surplus": "surplus"
                                           })

    def initialize_spread(self):
        # Initialize outstanding bid and ask
        self.outstanding_bid = 0
        self.outstanding_bidder = None
        self.outstanding_ask = math.inf
        self.outstanding_asker = None
        self.traded = 0
        self.market_price = None

    def update_ob(self, bidder, price):
        if price > self.outstanding_bid:
            # Update the outstanding bid
            self.outstanding_bid = price
            self.outstanding_bidder = bidder

            if price > self.outstanding_ask:
                # a transaction happens
                contract_price = self.outstanding_ask
                self.execute_contract(contract_price)
                self.history.accept_bid(bidder, self.outstanding_asker,
                                        contract_price)
            else:
                self.history.submit_bid(bidder, price)
        else:
            # null order
            self.history.submit_null(bidder, None, price)

    def update_oa(self, asker, price):
        if price < self.outstanding_ask:
            # Update the outstanding ask
            self.outstanding_ask = price
            self.outstanding_asker = asker

            if price < self.outstanding_bid:
                contract_price = self.outstanding_bid
                self.execute_contract(contract_price)
                self.history.accept_ask(self.outstanding_bidder, asker,
                                        contract_price)
            else:
                # only updated the outstanding ask
                self.history.submit_ask(asker, price)
        else:
            # null order
            self.history.submit_null(None, asker, price)

    def execute_contract(self, contract_price):
        self.outstanding_bidder.buy(contract_price)
        self.outstanding_asker.sell(contract_price)
        self.market_price = contract_price
        self.traded = 1
        self.num_traded += 1

    def next_period(self, new_supply=None, new_demand=None):

        if self.num_traded == 0:
            self.no_trade = True

        if new_supply and new_demand:
            self.new_supply = new_supply
            self.new_demand = new_demand
            self.shifted = True
            self.shifted_period = self.num_period + 1

        if self.shifted:
            supply = self.new_supply
            demand = self.new_demand
        else:
            supply = self.supply
            demand = self.demand

        # Making sure the schedule is ordered as it was initialized.
        self.schedule.agents.sort(key=lambda x: x.unique_id)

        for i, cost in enumerate(supply.price_schedule):
            self.schedule.agents[i].good = supply.q_per_agent
            self.schedule.agents[i].value = cost
            self.schedule.agents[i].active = True

        for i, value in enumerate(demand.price_schedule):
            j = self.num_sellers + i
            self.schedule.agents[j].right = demand.q_per_agent
            self.schedule.agents[j].value = value
            self.schedule.agents[j].active = True

        self.num_period += 1
        self.num_traded = 0
        self.loc_period.append(self.num_step)
        self.history.next_period()

    def step(self):
        if self.traded == 1:
            self.initialize_spread()
        # print("step:", self.num_step)
        self.schedule.step()
        self.num_step += 1
        self.datacollector.collect(self)

    def plot_model(self):
        data = self.datacollector.get_model_vars_dataframe()
        data = data[data.Traded == 1]
        f = plt.figure(1)
        ax = f.add_subplot(111)
        plt.plot(data.MarketPrice)
        plt.axhline(y=self.supply.equilibrium_price,
                    color='black',
                    linestyle='dashed')
        plt.text(0.8,
                 0.9,
                 round(compute_efficiency(self), 3),
                 fontsize=20,
                 transform=ax.transAxes)
        for i in range(self.num_period):
            plt.axvline(x=self.loc_period[i],
                        color='black',
                        linestyle='dashed')
        plt.show()
Ejemplo n.º 14
0
Archivo: model.py Proyecto: Loek21/ABM
class FishingModel(Model):
    '''
    Lokta-Volterra Type Model for Fishermen and Fish
    '''
    food_bool = True
    no_fish_zone_bool = True
    quotum_bool = False
    no_fish_size = 0.25
    quotum = 3000
    step_count = 5000

    def __init__(
        self,
        height=40,
        width=40,
        initial_fish=300,
        initial_fishermen=100,
        initial_school_size=100,
        split_size=200,
        fish_reproduction_number=1.15,
        initial_wallet=100,
        catch_rate=0.5,
        max_load=30,
        full_catch_reward=100,
        initial_wallet_survival=4 * 12,
        beta_fisherman_spawn=1,
        energy_gain=5,
        energy_loss=1,
        track_n_rolling_gains=4 * 3,
        initial_energy=10,
        regrowth_time=10,
        food_bool=True,
        no_fish_zone_bool=False,
        quotum_bool=False,
        no_fish_size=0,
        quotum=0,
    ):
        super().__init__()

        # Initialization
        self.height = height
        self.width = width
        self.initial_fish = initial_fish
        self.initial_fishermen = initial_fishermen
        self.initial_wallet = initial_wallet
        self.initial_wallet_survival = initial_wallet_survival
        self.initial_school_size = initial_school_size
        self.cumulative_gain = 0

        # Booleans
        self.food_bool = food_bool
        self.no_fish_zone_bool = no_fish_size > 0
        self.quotum_bool = quotum > 0

        #  Fish
        self.fish_reproduction_number = fish_reproduction_number
        self.split_size = split_size
        self.fish_cap = 5 * initial_fish * initial_school_size
        self.this_avg_school_size = initial_school_size

        # Fisherman
        self.max_load = max_load
        self.full_catch_reward = full_catch_reward
        self.catch_rate = catch_rate * self.max_load
        if self.quotum_bool == True:
            self.yearly_quotum = quotum
        else:
            self.yearly_quotum = 1000000000
        self.total_yearly_caught = 0
        self.total_yearly_caught_prev = 8000
        self.recruitment_switch = True
        self.beta_fisherman_spawn = beta_fisherman_spawn
        self.this_avg_wallet = initial_wallet
        self.track_n_rolling_gains = track_n_rolling_gains

        # food
        self.energy_gain = energy_gain
        if not self.food_bool:
            self.energy_loss = 0
        else:
            self.energy_loss = energy_loss
        self.initial_energy = initial_energy
        self.regrowth_time = regrowth_time
        self.food_amount = height * width

        # No fish zone
        if self.no_fish_zone_bool:
            self.no_fish_size = int(no_fish_size *
                                    (1 / no_fish_size)**(1 / 2) * width)
        else:
            self.no_fish_size = 0

        # Add a schedule for fish and fishermen seperately to prevent race-conditions
        self.schedule_Fish = RandomActivation(self)
        self.schedule_Fisherman = RandomActivation(self)
        if self.food_bool:
            self.schedule_Food = RandomActivation(self)

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

        if self.food_bool:
            self.datacollector = DataCollector({
                "time":
                lambda m: self.schedule_Fish.time,
                "Fish schools":
                lambda m: self.schedule_Fish.get_agent_count(),
                "Fishermen":
                lambda m: self.schedule_Fisherman.get_agent_count(),
                "Average wallet":
                lambda m: self.this_avg_wallet,
                "Average school size":
                lambda m: self.this_avg_school_size,
                "Total fish":
                lambda m: self.schedule_Fish.get_agent_count() * self.
                this_avg_school_size * 0.01,
                "Available food":
                lambda m: self.food_amount,
                "Cumulative gain":
                lambda m: self.cumulative_gain,
                "Fish price":
                lambda m: self.full_catch_reward
            })
        else:
            self.datacollector = DataCollector({
                "time":
                lambda m: self.schedule_Fish.time,
                "Fish schools":
                lambda m: self.schedule_Fish.get_agent_count(),
                "Fishermen":
                lambda m: self.schedule_Fisherman.get_agent_count(),
                "Average wallet":
                lambda m: self.this_avg_wallet,
                "Average school size":
                lambda m: self.this_avg_school_size,
                "Total fish":
                lambda m: self.schedule_Fish.get_agent_count() * self.
                this_avg_school_size * 0.01,
                "Cumulative gain":
                lambda m: self.cumulative_gain,
                "Fish price":
                lambda m: self.full_catch_reward
            })

        # Keep a list of all agents
        self.agents = []

        # Create fish and fishermen
        self.init_population(Fish, self.initial_fish)
        self.init_population(Fisherman, self.initial_fishermen)
        if self.food_bool == True:
            self.init_food()

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

        # full run statistics
        self.fish_mean = -666
        self.fish_slope = -666
        self.fish_variance = -666

    def init_food(self):
        '''
        Fills grid with fish food
        '''
        for i in range(self.width):
            for j in range(self.height):
                self.new_agent(Food, (i, j), 0, 0, True, self.regrowth_time,
                               True, 0)

    def init_population(self, agent_type, n):
        '''
        Method that provides an easy way of making a bunch of agents at once.
        '''
        for i in range(int(n)):
            x = random.randrange(self.width)
            y = random.randrange(self.height)

            if agent_type == Fish:
                self.new_agent(agent_type, (x, y), self.initial_school_size,
                               self.initial_energy, True, 0, False,
                               self.energy_loss)
            else:
                self.new_agent(agent_type, (x, 0), 0, self.initial_wallet,
                               True, 0, False, 0)

    def new_agent(self, agent_type, pos, size, wallet, switch, regrowth_time,
                  food, energy_loss):
        '''
        Method that creates a new agent, and adds it to the correct scheduler.
        '''
        agent = agent_type(self.next_id(), self, pos, size, wallet, switch,
                           regrowth_time, food, energy_loss)

        self.grid.place_agent(agent, pos)
        self.agents.append(agent)

        getattr(self, f'schedule_{agent_type.__name__}').add(agent)

    def remove_agent(self, agent):
        '''
        Method that removes an agent from the grid and the correct scheduler.
        '''
        self.grid.remove_agent(agent)
        self.agents.remove(agent)
        getattr(self, f'schedule_{type(agent).__name__}').remove(agent)

    def recruit_fisherman(self):
        '''
        Method that spawns new fisherman based on the average gains of the existing fisherman.
        '''

        # make sure that at least one fisherman exists, who wouldn't try a new bussiness in a new field?
        if self.schedule_Fisherman.get_agent_count() == 0:
            self.init_population(Fisherman, 1)

        rolling_mean_gains = statistics.mean([
            statistics.mean(fisherman.rolling_gains)
            for fisherman in self.schedule_Fisherman.agents
        ])

        # add new fisherman proportional to the profitability
        if rolling_mean_gains > 0:
            n_new_fisherman = int(
                np.random.poisson(lam=rolling_mean_gains *
                                  self.beta_fisherman_spawn,
                                  size=1))
            self.init_population(Fisherman, n_new_fisherman)

    def get_fish_stats(self):
        '''
        Method that computes information about fishes.
        '''
        fish_size = [fish.size for fish in self.schedule_Fish.agents]
        if len(fish_size) == 0:
            self.this_avg_school_size = 0
        else:
            self.this_avg_school_size = sum(fish_size) / len(fish_size)

    def get_food_stats(self):
        '''
        Method that computes the current amount of available food.
        '''
        food_amount = 0
        for agent in self.schedule_Food.agents:
            if agent.food == True:
                food_amount += 1
        self.food_amount = food_amount

    def calc_fish_reproduction(self):
        """
        Calculates the reproduction ratio for the fish per year, which caps the total fish pop.
        """
        total_fish = self.schedule_Fish.get_agent_count(
        ) * self.this_avg_school_size

        percentage = 1 - (self.fish_cap - total_fish) / self.fish_cap

        self.fish_reproduction_number = 0.2 / (1 + 0.00005**
                                               (-(percentage - 0.5))) + 1

        # the function doesn't cap at 1 exactly, so making sure it does here
        if percentage == 1:
            self.fish_reproduction_number = 1

    def get_fisherman_stats(self):
        '''
        Method that computes information about fisherman.
        '''
        if len(self.schedule_Fisherman.agents) == 0:
            self.this_avg_wallet = 0
        else:
            fisherman_wallet = [
                fisherman.wallet
                for fisherman in self.schedule_Fisherman.agents
            ]
            if len(fisherman_wallet) == 0:
                self.this_avg_wallet = 0
            else:
                self.this_avg_wallet = sum(fisherman_wallet) / len(
                    fisherman_wallet)

    def get_fish_price(self):
        '''
        Method for computing reward for fish based on number of previous fish caught
        '''
        if (self.schedule_Fish.time + 1) % (4 * 12) == 0:

            if self.total_yearly_caught == 0:
                self.full_catch_reward = 20
            else:
                self.full_catch_reward = 100 * self.total_yearly_caught_prev / self.total_yearly_caught
                if self.full_catch_reward < 20:
                    self.full_catch_reward = 20
                if self.full_catch_reward > 250:
                    self.full_catch_reward = 250
                if self.total_yearly_caught_prev == 0:
                    self.full_catch_reward = 250
                if self.total_yearly_caught == 0:
                    self.full_catch_reward = 250

            self.total_yearly_caught_prev = self.total_yearly_caught
            self.total_yearly_caught = 0
            self.recruitment_switch = True

    def get_model_stats(self):
        '''
        Method for computing full model run statistics
        '''
        final_data = self.datacollector.get_model_vars_dataframe()
        final_total_fish = final_data["Total fish"]

        mod = sm.OLS(final_total_fish, sm.add_constant(final_data["time"]))
        res = mod.fit()

        self.fish_mean = res.params[0]
        self.fish_slope = res.params[1]
        self.fish_variance = np.var(final_total_fish)

    def step(self):
        '''
        Method that calls the step method for each of the sheep, and then for each of the wolves.
        '''
        if self.food_bool == False:
            self.calc_fish_reproduction()
        self.get_fish_price()

        self.schedule_Fish.step()
        self.schedule_Fisherman.step()

        self.get_fish_stats()
        self.get_fisherman_stats()

        if self.food_bool == True:
            self.schedule_Food.step()
            self.get_food_stats()

        if self.recruitment_switch == True and (self.schedule_Fish.time +
                                                1) % 4 * 3 == 0:
            self.recruit_fisherman()

        # Save the statistics
        if (self.schedule_Fish.time + 4 * 6) % (4 * 12) == 0:
            self.datacollector.collect(self)

    def run_model(self, step_count):
        '''
        Method that runs the model for a specific amount of steps.
        '''

        for i in range(step_count):

            if self.total_yearly_caught >= self.yearly_quotum:
                self.recruitment_switch = False

            self.step()

        self.get_model_stats()
Ejemplo n.º 15
0
class EvacuationModel(Model):
    """A Mesa ABM model to simulate evacuation during a flood

    Args:
        hazard: Spatial table of flood hazard zones in WGS84
        output_path: Path to output files without extension
        domain: Polygon used to select OSM data, required if the graph, agents or targets are not specified
        target_types: List of OSM amenity values to use as targets, defaults to school
        network: Undirected network generated from OSM road network
        targets: Spatial table of OSM amenities
        target_capacity: The number of agents that can be evacuated to each target
        agents: Spatial table of agent starting locations
        seed: Seed value for random number generation

    Attributes:
        output_path (str): Path to output files without extension
        schedule (RandomActivation): Scheduler which activates each agent once per step,
            in random order, with the order reshuffled every step
        hazard (GeoDataFrame): Spatial table of flood hazard zones in WGS84
        G (Graph): Undirected network generated from OSM road network
        nodes (GeoDataFrame): Spatial table of nodes in G
        edges (GeoDataFrame): Spatial table edges in G
        grid (NetworkGrid): Network grid for agents to travel around based on G
        data_collector (DataCollector): Stores the model state at each time step
        target_nodes (Series): Series of nodes to evacuate to
        target_capacity (int): The number of agents that can be evacuated to each target
        igraph: Duplicate of G as an igraph object to speed up routing

    """
    def __init__(
            self,
            hazard: GeoDataFrame,
            output_path: str,
            domain: Optional[Polygon] = None,
            target_types: Iterable[str] = tuple(['school']),
            network: Optional[Graph] = None,
            targets: Optional[GeoDataFrame] = None,
            target_capacity: int = 100,
            agents: Optional[GeoDataFrame] = None,
            seed: Optional[int] = None):
        super().__init__()
        self._seed = seed
        self.output_path = output_path

        self.hazard = hazard
        self.schedule = RandomActivation(self)
        self.target_capacity = target_capacity

        if network is None:
            self.G = osmnx.graph_from_polygon(domain, simplify=False)
            self.G = self.G.to_undirected()
        else:
            self.G = network

        self.nodes: GeoDataFrame
        self.edges: GeoDataFrame
        self.nodes, self.edges = osmnx.save_load.graph_to_gdfs(self.G)

        if agents is None:
            agents = GeoDataFrame(geometry=create_footprints_gdf(domain).centroid)

        if targets is None:
            targets = osmnx.pois_from_polygon(domain, amenities=list(target_types))
            # Query can return polygons as well as points, only using the points
            targets = targets[targets.geometry.geom_type == 'Point']

        output_gpkg = output_path + '.gpkg'

        driver = 'GPKG'

        targets.crs, agents.crs = [self.nodes.crs] * 2

        nodes_tree = cKDTree(np.transpose([self.nodes.geometry.x, self.nodes.geometry.y]))

        # Prevents warning about CRS not being the same
        self.hazard.crs = self.nodes.crs
        self.hazard.to_file(output_gpkg, layer='hazard', driver=driver)

        agents_in_hazard_zone: GeoDataFrame = sjoin(agents, self.hazard)
        agents_in_hazard_zone = agents_in_hazard_zone.loc[~agents_in_hazard_zone.index.duplicated(keep='first')]
        agents_in_hazard_zone.geometry.to_file(output_gpkg, layer='agents', driver=driver)

        assert len(agents_in_hazard_zone) > 0, 'There are no agents within the hazard zone'

        targets_in_hazard_zone: GeoDataFrame = sjoin(targets, self.hazard)
        targets_in_hazard_zone = targets_in_hazard_zone.loc[~targets_in_hazard_zone.index.duplicated(keep='first')]

        targets_outside_hazard_zone = targets[~targets.index.isin(targets_in_hazard_zone.index.values)]
        targets_outside_hazard_zone.to_file(output_gpkg, layer='targets', driver=driver)

        assert len(targets_outside_hazard_zone) > 0, 'There are no targets outside the hazard zone'

        _, node_idx = nodes_tree.query(
            np.transpose([agents_in_hazard_zone.geometry.x, agents_in_hazard_zone.geometry.y]))

        _, target_node_idx = nodes_tree.query(
            np.transpose([targets_outside_hazard_zone.geometry.x, targets_outside_hazard_zone.geometry.y]))

        for (_, row), nearest_node in zip(targets_outside_hazard_zone.iterrows(), self.nodes.index[target_node_idx]):
            if not self.G.has_node(row.osmid):
                self.G.add_edge(nearest_node, row.osmid, length=0)
                self.G.nodes[row.osmid]['osmid'] = row.osmid
                self.G.nodes[row.osmid]['x'] = row.geometry.x
                self.G.nodes[row.osmid]['y'] = row.geometry.y

        self.nodes, self.edges = osmnx.save_load.graph_to_gdfs(self.G)

        self.nodes[['osmid', 'geometry']].to_file(output_gpkg, layer='nodes', driver=driver)
        self.edges[['osmid', 'geometry']].to_file(output_gpkg, layer='edges', driver=driver)

        output_gml = output_path + '.gml'
        osmnx.nx.write_gml(self.G, path=output_gml)
        self.igraph = igraph.read(output_gml)

        self.target_nodes = targets_outside_hazard_zone.osmid

        self.grid = NetworkGrid(self.G)

        # Create agents
        for i, idx in enumerate(node_idx):
            a = agent.EvacuationAgent(i, self)
            self.schedule.add(a)
            self.grid.place_agent(a, self.nodes.index[idx])
            a.update_route()
            a.update_location()

        self.data_collector = DataCollector(
            model_reporters={
                'evacuated': evacuated,
                'stranded': stranded
            },
            agent_reporters={'position': 'pos',
                             'reroute_count': 'reroute_count',
                             'lat': 'lat',
                             'lon': 'lon',
                             'highway': 'highway',
                             'status': status})

    def step(self):
        """Advances the model by one step and then stores the current state in data_collector"""
        self.schedule.step()
        self.data_collector.collect(self)

    def run(self, steps: int):
        """Runs the model for the given number of steps`

        Args:
            steps: number of steps to run the model for
        Returns:
            DataFrame: the agent vars dataframe
        """
        self.data_collector.collect(self)
        for _ in range(steps):
            self.step()
            if self.data_collector.model_vars['evacuated'][-1] + self.data_collector.model_vars['stranded'][-1] == len(
                    self.schedule.agents):
                # Continue for 5 steps after all agents evacuated or stranded
                for _ in range(5):
                    self.step()
                break
        self.data_collector.get_agent_vars_dataframe().astype({'highway': pd.Int64Dtype()}).to_csv(
            self.output_path + '.agent.csv')
        self.data_collector.get_model_vars_dataframe().to_csv(self.output_path + '.model.csv')
        return self.data_collector.get_agent_vars_dataframe()
Ejemplo n.º 16
0
class LoveMatch(Model):
    '''
    Love-match market Model: 
    
    En este modelo, cada individuo recorre de manera aleatoria el lugar, al encontrarse con un match (agente del sexo opuesto con parámetros de belleza y riqueza coincidentes con lo deseado) desaparece del modelo. 
    El objetivo es observar la distribución de perfiles de belleza y riqueza a lo largo del tiempo hasta ver quienes no logran encontrar pareja. 
    '''
    def __init__(
        self,
        height=50,
        width=50,
        density=0.8,
        HM_pc=0.2,
        entry_rate=1,
        max_agents=750
    ):  # Aquí establecemos el tamaño del Grid donde se desarrolla el modelo, además de los parámetros iniciales.
        self.height = height
        self.width = width
        self.density = density
        self.HM_pc = HM_pc

        self.entry_rate = 5

        self.schedule = RandomActivation(self)
        self.grid = MultiGrid(height, width, torus=False)
        self.max_agents = max_agents
        self.parejas = 0
        self.hombres = 0
        self.mujeres = 0
        self.unhappy = 0
        self.idcounter = 0

        # En esta sección, etiquetamos a cada agente según su tipo

        for cell in self.grid.coord_iter():
            x = cell[1]
            y = cell[2]
            if self.random.random() < self.density:
                if self.random.random() < self.HM_pc:
                    gender = 1
                    self.hombres += 1
                else:
                    gender = 0
                    self.mujeres += 1
                #Creamos a cada agente y asignamos su ID, cad vez que se crea un agente, se agrega uno al contador de ID's
                #Nota: La distribución de las características las modelamos con una distribución log-normal. Esto nos permite tener solo valores positivos y este ranking de belleza/riqueza se concentra de 0 a 1
                self.idcounter += 1
                agent = miAgente((x, y),
                                 self,
                                 gender,
                                 beauty=np.random.lognormal(0.5, 0.30),
                                 wealth=np.random.lognormal(0.5, 0.30),
                                 desired_beauty=np.random.lognormal(0.5, 0.3),
                                 desired_wealth=np.random.lognormal(0.5, 0.3),
                                 time_to_critical=random.randint(10, 30),
                                 sojourn=-1,
                                 is_critical=0,
                                 myid=self.idcounter)
                #coloca a los agentes en el modelo
                self.schedule.add(agent)
                self.grid.place_agent(agent, (x, y))
        #Corre el modelo
        self.running = True
        #Colecciona los datos relevantes para el agente y para el modelo
        self.datacollector = DataCollector(model_reporters={
            'density': 'density',
            'parejas': 'parejas',
            'unhappy': 'unhappy',
            'hombres': 'hombres',
            'mujeres': 'mujeres'
        },
                                           agent_reporters={
                                               'myid': 'myid',
                                               'wealth': 'wealth',
                                               'gender': 'gender',
                                               'beauty': 'beauty',
                                               'desired_beauty':
                                               'desired_beauty',
                                               'desired_wealth':
                                               'desired_wealth',
                                               'time_to_critical':
                                               'time_to_critical',
                                               'is_critical': 'is_critical',
                                               'sojourn': 'sojourn'
                                           })
        self.datacollector.collect(self)

    def update(self):
        if self.schedule.get_agent_count() < self.max_agents:
            for i in range(self.entry_rate):
                x = self.random.randrange(self.grid.width)
                y = self.random.randrange(self.grid.height)
                if self.random.random() < self.HM_pc:
                    gender = 1
                    self.hombres += 1
                else:
                    gender = 0
                    self.mujeres += 1

                agent = miAgente(i,
                                 self,
                                 gender,
                                 beauty=random.g(4, 2),
                                 wealth=random.gauss(4, 3),
                                 desired_beauty=random.gauss(4, 3),
                                 desired_wealth=random.gauss(3, 2),
                                 time_to_critical=random.gauss(20, 5),
                                 sojourn=-1,
                                 is_critical=0)
                self.schedule.add(agent)
                self.grid.place_agent(agent, (x, y))

    def step(
        self
    ):  # Este step permite que el modelo siga corriendo hasta que todos los agentes tengan pareja
        self.schedule.step()
        # Por fines gráficos, recolectamos la información sobre la cantidad de parejas
        self.datacollector.collect(self)

        ### Guarda la información relevante dentro de tablas en csv's.
        self.datacollector.get_agent_vars_dataframe().to_csv("test_me_a.csv")
        self.datacollector.get_model_vars_dataframe().to_csv("test_me_m.csv")

        ### Finalmente, el modelo se detiene si el número de agentes es cero
        if self.schedule.get_agent_count() == 0:
            self.running = False
Ejemplo n.º 17
0
class Modelo(Model):
    #Algunas constantes
    SUSCEPTIBLE = 0
    EXPUESTO = 1
    INFECTADO = 2
    RECUPERADO = 3
    salud_to_str = {
        0: 'Susceptible',
        1: 'Expuesto',
        2: 'Infectado',
        3: 'Recuperado'
    }
    pp_dia = 4  ## Son los pasos dados por dia simulado

    def __init__(self,
                 world_object,
                 agent_object,
                 params,
                 ind_attrs,
                 rand_seed=None):
        super().__init__()
        self.rand = Random(rand_seed)
        self.params = params
        self.mundo = world_object(self, agent_object)
        self.movilidad = obtener_movilidad()
        self.dia_cero = params['dia_cero']
        #self.prop_inf_exp = params['prop_inf_exp'] #Proporcion entre infectaros y suceptibles a la fecha
        self.un_dia = datetime.timedelta(days=1)
        self.ind_attrs = ind_attrs
        self.schedule = RandomActivation(self)
        self.generar_regiones()
        self.dia = 0
        self.n_paso = 0

        ## Se define el grid que se representará en la
        #self.grid = self.ciudad.nodes['ciudad']['espacio']
        model_reporters = {
            'Fecha': lambda x: x.dia_cero + x.dia * x.un_dia,
            'Susceptibles': self.conteo_func(self.SUSCEPTIBLE),
            'Expuestos': self.conteo_func(self.EXPUESTO),
            'Infectados': self.conteo_func(self.INFECTADO),
            'Recuperados': self.conteo_func(self.RECUPERADO)
        }
        reg_reporters = {k: self.conteo_por_reg(k) for k in self.regiones}
        self.datacollector = DataCollector({
            **model_reporters,
            **reg_reporters
        })
        self.conteo_instantaneo = self.conteo()

    def generar_regiones(self):
        datos = self.leer_regiones('Datos/datos.pk')
        conexiones = self.generar_lista_de_aristas('Datos/adyacencia.pk',
                                                   list(datos.keys()))
        #infectados = self.obtener_infectados('Datos/infectados.csv',
        #                                     list(datos.keys()))
        #historico = leer_historico()
        #infectados = historico[(self.dia_cero, 'Activos')]
        #fecha = self.params['dia_cero']################################3
        ids_start = 0
        self.regiones = {}
        for region in datos:
            #if region not in seleccionadas: continue
            self.regiones[region] = datos[region]
            tamano = self.params['area']
            pob = ceil(self.regiones[region]['pob'] //
                       self.params['inds_x_agente'])
            ids = [i for i in range(ids_start, ids_start + pob)]
            ids_start += pob

            individuos = self.mundo.generar_individuos(pob,
                                                       ids=ids,
                                                       attrs=self.ind_attrs)
            """
            n_infectados = ceil(infectados[region]/self.params['inds_x_agente'])\
                            if infectados.get(region, None) is not None else 0
            n_susceptibles = ceil(n_infectados*self.prop_inf_exp)
            #print(f'{region}: {pob} agentes, {n_infectados} infectados, {n_susceptibles} susceptibles')
            """
            if region == 'Mérida':
                for i in range(self.params['expuestos_iniciales']):
                    individuos[i].salud = self.EXPUESTO

            for ind in individuos:
                """
                if n_infectados>0:
                    ind.salud = self.INFECTADO
                    n_infectados -= 1
                    #print(f'\tSe agrega un infectado ind {ind.unique_id}')
                elif n_infectados==0 and n_susceptibles>0:
                    ind.salud = self.EXPUESTO
                    n_susceptibles -= 1
                    #print(f'\tSe agrega un expuesto ind {ind.unique_id}')
                """
                self.schedule.add(ind)

            #print(f'La región {region} tiene {len(individuos)}')
            self.mundo.crear_nodo(region,
                                  'municipio',
                                  ocupantes=individuos,
                                  tamano=tamano,
                                  ind_pos_def='aleatorio')
        #print('Se crean las aristas')
        self.mundo.add_weighted_edges_from(conexiones, weight='peso')
        #print([a.salud for a in self.schedule.agents if a.salud==self.EXPUESTO])
        #posiciones = {k: list(self.regiones[k]['centro'])[::-1] for k in self.regiones}
        #self.mundo.visualizar(pos = posiciones, with_labels = True)

    def norm_coord(self, coord):
        coord = np.array(coord)
        esq1 = np.array([21.670833, -90.621414])
        esq2 = np.array([19.546208, -87.449881])
        delta = esq2 - esq1
        return (coord - esq1) / delta

    def conteo(self):
        #Una función para contar los casos actuales en la ciudad
        self.conteo_instantaneo = [0, 0, 0, 0]
        for a in self.schedule.agents:
            self.conteo_instantaneo[a.salud] += 1
        return self.conteo_instantaneo

    def conteo_func(self, tipo):
        def contar(modelo):
            return modelo.conteo_instantaneo[tipo]

        return contar

    def conteo_por_reg(self, reg):
        def contar(modelo):
            ags = modelo.mundo.obtener_agentes_en_nodo(reg)
            conteo = [0, 0, 0, 0]
            for a in ags:
                conteo[a.salud] += 1
            return conteo

        return contar

    def leer_regiones(self, path):
        with open(path, 'rb') as f:
            datos = pk.load(f)
        return datos

    def generar_lista_de_aristas(self, path, regiones):
        conexiones = []
        with open(path, 'rb') as f:
            datos = pk.load(f)
            assert len(datos) == len(
                regiones), f'{len(datos)}!={len(regiones)}'
            a_agregar = []
            for region in datos:
                a_agregar = [(region, nueva, peso)\
                               for nueva, peso in datos[region]]
                conexiones.extend(a_agregar)
        return conexiones

    def obtener_infectados(self, path, regiones):
        infectados = {}
        with open(path, 'r') as f:
            for line in f.readlines()[4:]:
                datos = line.split(',')
                if datos[1] not in regiones:
                    print(f'{datos[1]} no está en regiones')
                else:
                    infectados[datos[1]] = int(datos[5])
        return infectados

    def step(self):
        self.dia = self.n_paso // self.pp_dia  #es el momento del dia

        if self.dia == 4:
            ##En el cuarto día, que corresponde al primer caso en
            #Yucatán, se planta un infectado. Esto para asegurar que
            #siempre habrá un infectado
            agentes = self.mundo.obtener_agentes_en_nodo('Mérida')
            agentes[0].salud = self.INFECTADO

        self.conteo()
        self.datacollector.collect(self)
        self.schedule.step()
        self.n_paso += 1

    def correr(self, n_steps, show=False):
        bloques = int(n_steps * 0.1)
        if show: print('---- Corriendo simulación ----')
        for i in range(n_steps):
            self.step()
            if show and int(i % bloques) == 0:
                print('%d%% ... ' % (int(i / n_steps * 100)), end='\n')
        if show: print('100%')

    def plot(self, names=None):
        data = self.datacollector.get_model_vars_dataframe()
        if names is None:
            data.plot()
        elif isinstance(names, list):
            data[names].plot()
        else:
            print('Se debe ingresar los nombres de las columnas en una lista')
        plt.show(block=True)
Ejemplo n.º 18
0
class DCGame(Model):
    def __init__(self, adjMat, numVisibleColorNodes, numAdversarialNodes,
                 inertia):
        self.adjMat = adjMat
        self.numVisibleColorNodes = numVisibleColorNodes
        self.numAdversarialNodes = numAdversarialNodes
        # self.adversarialNodes = []
        self.visibleColorNodes = []
        self.regularNodes = []
        self.schedule = RandomActivation(self)
        self.numAgents = len(adjMat)
        self.inertia = inertia
        # if there are 20 consensus colors then a
        # terminal state is reached
        self.terminate = False
        self.time = 0
        # logging information
        self.log = Log()

        ##  temporarily added this for figuring out
        ##  why visible nodes have no help
        self.hasConflict = False

        # convert adjMat to adjList
        def getAdjList(adjMat):
            adjList = {key: [] for key in range(self.numAgents)}
            for node in range(self.numAgents):
                adjList[node] = [
                    idx for idx, value in enumerate(adjMat[node])
                    if value == True
                ]
            return adjList

        self.adjList = getAdjList(self.adjMat)

        ############# designate adversarial #############
        # (node, degree)
        node_deg = [(idx, count(adjMat[idx])) for idx in range(self.numAgents)]
        # select the top-k nodes with largest degrees as adversarial
        node_deg.sort(key=lambda x: x[1], reverse=True)
        self.adversarialNodes = [
            item[0] for item in node_deg[:self.numAdversarialNodes]
        ]

        ############# designate visible nodes #############
        availableNodes = shuffled(node_deg[self.numAdversarialNodes:])
        self.visibleColorNodes = [
            item[0] for item in availableNodes[:self.numVisibleColorNodes]
        ]

        self.regularNodes = [
            n for n in range(self.numAgents) if n not in self.adversarialNodes
        ]
        # make sure we have 20 regular nodes
        assert len(self.regularNodes) == 20

        # adversarial nodes and regular nodes should not overlap
        assert set(self.adversarialNodes) & set(self.regularNodes) == set()
        # visible nodes should belong to regular nodes
        assert set(self.visibleColorNodes) & set(self.regularNodes) == set(
            self.visibleColorNodes)

        # logging simulation configuration
        self.log.add("#visible nodes: " + str(self.visibleColorNodes))
        self.log.add("#adversarial nodes: " + str(self.adversarialNodes))
        self.log.add("#regular nodes: " + str(self.regularNodes) + '\n')

        ############# initialize all agents #############
        for i in range(self.numAgents):
            # if i is a visible node
            isVisibleNode = i in self.visibleColorNodes
            # if i is an adversarial
            isAdversarial = i in self.adversarialNodes
            # make sure adversarial nodes are not intersected with visible nodes
            assert isVisibleNode & isAdversarial == False

            neighbors = self.adjList[i]

            # visible color nodes in i's neighbors
            vNode = list(set(neighbors) & set(self.visibleColorNodes))
            # if i == 6:
            #     print(vNode)
            inertia = self.inertia

            # print("Add agent:", (i, visibleNode, adversarial, neighbors, visibleColorNodes))
            a = GameAgent(i, isVisibleNode, isAdversarial, neighbors, vNode,
                          inertia, self)
            self.schedule.add(a)

        # instantiate all nodes' neighbors and visibleColorNodes
        for agent in self.schedule.agents:
            agent.instantiateNeighbors(self)
            agent.instantiateVisibleColorNodes(self)

        self.datacollector = DataCollector(
            model_reporters={
                "red": getRed,
                "green": getGreen
            },
            agent_reporters={"agent_color": lambda a: a.color})

    # simulate the whole model for one step
    def step(self):
        # # # if either red or green reaches consensus, terminates!
        # # in terminal state we do not collect data
        if not self.terminate:
            self.datacollector.collect(self)
            self.schedule.step()
        return self.terminate

    def simulate(self, simulationTimes):
        for i in range(simulationTimes):
            # update model's time
            self.updateTime(i)
            terminate = self.step()
            if terminate:
                break
        # output log file to disk
        self.log.outputLog('result/simResult.txt')
        simulatedResult = self.datacollector.get_model_vars_dataframe()
        return simulatedResult

    # update model's clock
    def updateTime(self, t):
        self.time = t

    def setTerminal(self):
        assert self.terminate == False
        self.terminate = True

    def addRecord(self, msg):
        self.log.add(msg)

    # for degub purpose only
    def outputAdjMat(self, path):
        with open(path, 'w') as fid:
            for line in self.adjMat:
                # convert list of boolean values to string values
                tline = ["1" if item else "0" for item in line]
                fid.write(' '.join(tline) + '\n')
Ejemplo n.º 19
0
class ForestFire(Model):
    """
    Simple Forest Fire model.
    """
    def __init__(self,
                 height=100,
                 width=100,
                 density=0.65,
                 server=True,
                 num_steps=1000):
        """
        Create a new forest fire model.

        Args:
            height, width: The size of the grid to model
            density: What fraction of grid cells have a tree in them.
        """
        # Initialize model parameters
        self.height = height
        self.width = width
        self.density = density
        self.server = server
        # Set up model objects
        self.schedule = RandomActivation(self)
        self.grid = Grid(height, width, torus=False)
        self.num_steps = num_steps

        self.datacollector = DataCollector({
            "Fine":
            lambda m: self.count_type(m, "Fine"),
            "On Fire":
            lambda m: self.count_type(m, "On Fire"),
            "Burned Out":
            lambda m: self.count_type(m, "Burned Out")
        })

        # Place a tree in each cell with Prob = density
        for (contents, x, y) in self.grid.coord_iter():
            if self.random.random() < self.density:
                # Create a tree
                new_tree = TreeCell((x, y), self)
                # Set all trees in the first column on fire.
                if x == 0:
                    new_tree.condition = "On Fire"
                self.grid._place_agent((x, y), new_tree)
                self.schedule.add(new_tree)

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

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

        # Halt if no more fire
        if self.count_type(self, "On Fire") == 0:
            self.running = False

    @staticmethod
    def count_type(model, tree_condition):
        """
        Helper method to count trees in a given condition in a given model.
        """
        count = 0
        for tree in model.schedule.agents:
            if tree.condition == tree_condition:
                count += 1
        return count

    def run_model(self,
                  n=None,
                  export_agent_data=False,
                  export_model_data=False):

        if self.server == False:
            if not n:
                for _ in range(self.num_steps):
                    self.step()
                if export_agent_data:
                    return self.datacollector.get_agent_vars_dataframe()
                elif export_model_data:
                    return self.datacollector.get_model_vars_dataframe()
                elif export_model_data and export_agent_data:
                    return self.datacollector.get_model_vars_dataframe, self.datacollector.get_agent_vars_dataframe
            else:
                self.num_steps = n
                for _ in range(self.num_steps):
                    self.step()
                # if export_agent_data:
                #     return self.datacollector.get_agent_vars_dataframe()
                # elif export_model_data:
                #     return self.datacollector.get_model_vars_dataframe()
                # elif export_model_data == True and export_agent_data == True:
                #     return self.datacollector.get_model_vars_dataframe, self.datacollector.get_agent_vars_dataframe
                return self
        else:
            from .server import server

            server.launch()
Ejemplo n.º 20
0
class BankReservesModel(Model):

    # grid height
    grid_h = 20
    # grid width
    grid_w = 20
    """init parameters "init_people", "rich_threshold", and "reserve_percent"
       are all UserSettableParameters"""
    def __init__(
        self,
        height=grid_h,
        width=grid_w,
        init_people=2,
        rich_threshold=10,
        reserve_percent=50,
    ):
        self.height = height
        self.width = width
        self.init_people = init_people
        self.schedule = RandomActivation(self)
        self.grid = MultiGrid(self.width, self.height, torus=True)
        # rich_threshold is the amount of savings a person needs to be considered "rich"
        self.rich_threshold = rich_threshold
        self.reserve_percent = reserve_percent
        # see datacollector functions above
        self.datacollector = DataCollector(
            model_reporters={
                "Rich": get_num_rich_agents,
                "Poor": get_num_poor_agents,
                "Middle Class": get_num_mid_agents,
                "Savings": get_total_savings,
                "Wallets": get_total_wallets,
                "Money": get_total_money,
                "Loans": get_total_loans
            },
            agent_reporters={"Wealth": lambda x: x.wealth})

        # create a single bank for the model
        self.bank = Bank(1, self, self.reserve_percent)

        # create people for the model according to number of people set by user
        for i in range(self.init_people):
            # set x coordinate as a random number within the width of the grid
            x = random.randrange(self.width)
            # set y coordinate as a random number within the height of the grid
            y = random.randrange(self.height)
            p = Person(i, (x, y), self, True, self.bank, self.rich_threshold)
            # place the Person object on the grid at coordinates (x, y)
            self.grid.place_agent(p, (x, y))
            # add the Person object to the model schedule
            self.schedule.add(p)

        self.running = True

    def step(self):
        # collect data
        self.datacollector.collect(self)
        # tell all the agents in the model to run their step function
        self.schedule.step()
        # if the step count is in the list then create a data file of model state
        if self.schedule.steps in [100, 500, 1000]:
            model_data = self.datacollector.get_model_vars_dataframe()
            model_data.to_csv("BankReservesModel_Step_Data_Single_Run" +
                              str(self.schedule.steps) + ".csv")

    def run_model(self):
        for i in range(self.run_time):
            self.step()
Ejemplo n.º 21
0
class MModel(Model):
    #A model with some number of agents
    # Inherits 'Model' from Mesa, and 'Location' from other class
    #this allow instances of this class to run the 'Location' methods
    def __init__(self, N, width, height):
        super().__init__()
        self.num_agents = N
        self.grid = MultiGrid(width, height, True)
        self.schedule = RandomActivation(self)
        self.running = True

        # create the agents
        for i in range(self.num_agents):
            a = MAgent(i, self)
            self.schedule.add(a)

            # add agents to random point on grid
            x = random.randrange(self.grid.width)
            y = random.randrange(self.grid.height)
            self.grid.place_agent(a, (x, y))
            print('place agent in pos', a.pos)

            #collect data for charts
            self.datacollector = DataCollector(
                agent_reporters={
                    "Wealth": "wealth",
                    "Location": "xy"
                },  # An agent level stat collector
                model_reporters={
                    'Total_Wealth_All_Models': total_wealth,
                    'Assignment': assignments
                })  # model level stat collector

    def pay(self):

        # obtain the amounts of agents per pool, work out how much they are paid
        #then assign new $$ to them based on the pool they are in

        model_wealth = self.datacollector.get_model_vars_dataframe()
        assign = model_wealth['Assignment'].iloc[-1]
        #print ( 'There are the numbers of agents per poll', assign)

        p = Payout(assign)
        self.pay_list = p.pay_out()
        #print ( 'The is amount agents receive on a per pool Payout', self.pay_list)

        for agent in self.schedule.agents:
            #print (agent.unique_id, agent.wealth, agent.pos  , 'agents stats')

            if agent.pos == (1, 0):
                #Low Pool
                agent.wealth = agent.wealth + self.pay_list[0]

            if agent.xy == (2, 0):
                #Stable Pool
                agent.wealth = agent.wealth + self.pay_list[1]

            if agent.xy == (3, 0):
                #high
                agent.wealth = agent.wealth + self.pay_list[2]

        return

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

        #calculate model per model data  each step
        print('Model Wealth =', model_wealth(self))
Ejemplo n.º 22
0
class DCGame(Model):
    def __init__(self, adjMat, G, numVisibleColorNodes, numAdversarialNodes,
                 inertia, beta, delay, visibles, adversaries):
        self.adjMat = adjMat
        self.numVisibleColorNodes = numVisibleColorNodes
        self.numAdversarialNodes = numAdversarialNodes
        # self.adversarialNodes = []
        self.visibleColorNodes = []
        self.regularNodes = []
        self.schedule = FollowVisibleActivation(self)
        self.numAgents = len(adjMat)
        self.inertia = inertia
        # if there are 20 consensus colors then a
        # terminal state is reached
        self.terminate = False
        self.time = 0
        # logging information
        self.log = Log()

        ##  temporarily added this for figuring out
        ##  why visible nodes have no help
        self.hasConflict = False

        # randomize regular players (exclude visibles)
        # decision
        self.beta = beta

        # a amount of time to delay ordinary players' decision
        # ordinary players = players who are neither visibles
        # nor has any visibles in their neighbor
        self.delay = delay

        # total number of color changes in a game
        self.colorChanges = 0

        # addded by Yifan
        self.reach_of_adversaries = 0
        self.reach_of_visibles = 0
        self.total_cnt_of_adversaries = 0
        self.total_cnt_of_visibles = 0
        self.graph = G

        # convert adjMat to adjList
        def getAdjList(adjMat):
            adjList = {key: [] for key in range(self.numAgents)}
            for node in range(self.numAgents):
                #adjList[node] = [idx for idx, value in enumerate(adjMat[node]) if value == True]
                adjList[node] = [
                    idx for idx, value in enumerate(adjMat[node])
                    if value == 'True'
                ]
            return adjList

        self.adjList = getAdjList(self.adjMat)

        #return the subset of L availableNodes in G with the largest number of distinct neighbors
        def getSubsetWithMaxDistinctNeighbors(availableNodes, G, L):
            acc = []
            max_cnt = 0
            local_cnt = 0
            hasBeenConsidered = [False for i in range(self.numAgents)]
            graph = nx.convert.to_dict_of_lists(G)
            for subset in itertools.combinations(availableNodes, L):
                upper_bound = 0
                for agent in subset:
                    upper_bound += len(graph[agent])
                if upper_bound < max_cnt:
                    continue
                # compute reach
                for agent in subset:
                    for neighbor in G.neighbors(agent):
                        if neighbor not in subset and hasBeenConsidered[
                                neighbor] == False:
                            local_cnt += 1
                            hasBeenConsidered[neighbor] = True
                if local_cnt > max_cnt:
                    max_cnt = local_cnt
                    acc.clear()
                    for agent in subset:
                        acc.append(agent)
                local_cnt = 0
                hasBeenConsidered = [False for i in range(self.numAgents)]
            return acc

        ############# designate visible #############
        # node_deg = [(idx, count(adjMat[idx])) for idx in range(self.numAgents)]
        # availableNodes = [item[0] for item in node_deg]
        # random.shuffle(availableNodes)
        # availableNodes.sort(key=lambda x : x)
        # self.visibleColorNodes = getSubsetWithMaxDistinctNeighbors(availableNodes, G, numVisibleColorNodes)
        # self.visibleColorNodes = [item for item in availableNodes[:self.numVisibleColorNodes]]
        self.visibleColorNodes = visibles
        # for visibleNode in self.visibleColorNodes:
        #     availableNodes.remove(visibleNode)

        ############# designate adversarial ###############
        # self.adversarialNodes = getSubsetWithMaxDistinctNeighbors(availableNodes, G, numAdversarialNodes)
        # self.adversarialNodes = [item for item in availableNodes[:self.numAdversarialNodes]]
        self.adversarialNodes = adversaries

        # ================ prev version: designate adversarial and visible nodes ===========
        # node_deg = [(idx, count(adjMat[idx])) for idx in range(self.numAgents)]
        # all_nodes = [item[0] for item in node_deg]
        # random.shuffle(node_deg)
        # self.adversarialNodes = [item[0] for item in node_deg[:self.numAdversarialNodes]]

        # reach_of_adversaries = 0
        # total_cnt_of_adversaries = 0
        # hasBeenReached = dict.fromkeys(all_nodes, False)
        # for adversarialNode in self.adversarialNodes:
        #     for neighbor in G.neighbors(adversarialNode):
        #         if neighbor not in self.adversarialNodes:
        #             total_cnt_of_adversaries += 1
        #         if neighbor not in self.adversarialNodes and hasBeenReached[neighbor] == False:
        #             reach_of_adversaries += 1
        #             hasBeenReached[neighbor] = True
        # self.reach_of_adversaries = reach_of_adversaries
        # self.total_cnt_of_adversaries = total_cnt_of_adversaries

        # ############# designate visible nodes #############
        # availableNodes = shuffled(node_deg[self.numAdversarialNodes:])
        # self.visibleColorNodes = [item[0] for item in availableNodes[:self.numVisibleColorNodes]]

        # reach_of_visibles = 0
        # total_cnt_of_visibles = 0
        # hasBeenReached = dict.fromkeys(all_nodes, False)
        # for visibleColorNode in self.visibleColorNodes:
        #     for neighbor in G.neighbors(visibleColorNode):
        #         if neighbor not in self.adversarialNodes and neighbor not in self.visibleColorNodes:
        #             total_cnt_of_visibles += 1
        #         if neighbor not in self.adversarialNodes and neighbor not in self.visibleColorNodes and hasBeenReached[neighbor] == False:
        #             reach_of_visibles += 1
        #             hasBeenReached[neighbor] = True
        # self.reach_of_visibles = reach_of_visibles
        # self.total_cnt_of_visibles = total_cnt_of_visibles

        # ===============================

        self.regularNodes = [
            n for n in range(self.numAgents) if n not in self.adversarialNodes
        ]
        # make sure we have 20 regular nodes
        # assert len(self.regularNodes) ==20

        assert set(self.adversarialNodes) & set(
            self.visibleColorNodes) == set()
        # adversarial nodes and regular nodes should not overlap
        assert set(self.adversarialNodes) & set(self.regularNodes) == set()
        # visible nodes should belong to regular nodes
        assert set(self.visibleColorNodes) & set(self.regularNodes) == set(
            self.visibleColorNodes)

        # logging simulation configuration
        self.log.add("#visible nodes: " + str(self.visibleColorNodes))
        self.log.add("#adversarial nodes: " + str(self.adversarialNodes))
        self.log.add("#regular nodes: " + str(self.regularNodes) + '\n')

        ############# initialize all agents #############
        for i in range(self.numAgents):
            # if i is a visible node
            isVisibleNode = i in self.visibleColorNodes
            # if i is an adversarial
            isAdversarial = i in self.adversarialNodes
            # make sure adversarial nodes are not intersected with visible nodes
            assert isVisibleNode & isAdversarial == False

            neighbors = self.adjList[i]

            # visible color nodes in i's neighbors
            vNode = list(set(neighbors) & set(self.visibleColorNodes))

            inertia = self.inertia
            beta = self.beta

            # print("Add agent:", (i, visibleNode, adversarial, neighbors, visibleColorNodes))
            a = GameAgent(i, isVisibleNode, isAdversarial, neighbors, vNode,
                          inertia, beta, self)
            self.schedule.add(a)

        # instantiate all nodes' neighbors and visibleColorNodes
        for agent in self.schedule.agents:
            agent.instantiateNeighbors(self)
            agent.instantiateVisibleColorNodes(self)

        self.datacollector = DataCollector(
            model_reporters={
                "red": getRed,
                "green": getGreen
            },
            agent_reporters={"agent_color": lambda a: a.color})

    def getReachOfAdversaries(self):
        return self.reach_of_adversaries

    def getReachOfVisibles(self):
        return self.reach_of_visibles

    def getTotalCntOfAdversaries(self):
        return self.total_cnt_of_adversaries

    def getTotalCntOfVisibles(self):
        return self.total_cnt_of_visibles

    # simulate the whole model for one step
    def step(self):
        # # # if either red or green reaches consensus, terminates!
        # # in terminal state we do not collect data
        if not self.terminate:
            self.datacollector.collect(self)
            self.schedule.step(self.delay)
        return self.terminate

    def simulate(self, simulationTimes):
        for i in range(simulationTimes):
            # update model's time
            # print("simulation time: " + str(i))
            self.updateTime(i)
            terminate = self.step()
            if terminate:
                break

        #added by Yifan
        isRegWhite = False
        # output log file to disk
        if not terminate:
            # did not reach consensus
            for agent in self.schedule.agents:
                if not agent.isAdversarial and not agent.isVisibleNode and agent.color == "white":
                    #at least one regular player remained white
                    isRegWhite = True

        self.log.outputLog('result/simResult.txt')
        simulatedResult = self.datacollector.get_model_vars_dataframe()
        # print(simulatedResult)
        return (simulatedResult, isRegWhite)

    # update model's clock
    def updateTime(self, t):
        self.time = t

    def setTerminal(self):
        assert self.terminate == False
        self.terminate = True

    def addRecord(self, msg):
        self.log.add(msg)

    # for degub purpose only
    def outputAdjMat(self, path):
        with open(path, 'w') as fid:
            for line in self.adjMat:
                # convert list of boolean values to string values
                tline = ["1" if item else "0" for item in line]
                fid.write(' '.join(tline) + '\n')
Ejemplo n.º 23
0
class WorldModel(Model):
    """
    Model for representing the world
    """

    def __init__(self):
        """
        Create a new WorldModel with the given parameters
        """
        super().__init__()
        # Read config.cfg
        config = configparser.ConfigParser()
        config.read('./config.ini')

        self.background_image_source = config.get('Grid', 'image', fallback='./delivery/visualization/images/a_city500x500.jpg')
        if type(self.background_image_source) is not str:
            print("[Configuration] The image is not valid.")
            sys.exit(1)

        try:
            test_background_image_source = self.background_image_source.split("/").pop()
            test_background_image_source = test_background_image_source.split(".")
            if test_background_image_source[len(test_background_image_source) - 1] != "jpg":
                raise ValueError
        except Exception:
            print("[Configuration] The image is not valid.")
            sys.exit(1)

        # Read landscape
        try:
            background_image = Image.open(self.background_image_source)
            background = background_image.load()
        except FileNotFoundError:
            print("[Configuration] The image could not be found.")
            sys.exit(1)

        # Configure schedule for UAVs and BaseStations
        self.schedule = Schedule(self)
        # Configure schedule for items
        self.item_schedule = Schedule(self)
        # Set parameters for ...
        # ... Grid
        self.width, self.height = background_image.size
        try:
            self.pixel_ratio = config.getint('Grid', 'pixel_ratio', fallback=10)
        except ValueError:
            print("[Configuration] The pixel_ratio is not valid.")
            sys.exit(1)

        try:
            self.max_altitude = config.getint('Grid', 'max_altitude', fallback=4)
        except ValueError:
            print("[Configuration] The max_altitude is not valid.")
            sys.exit(1)
        # ... BaseStations
        try:
            self.range_of_base_station = config.getint('Base_station', 'range_of_base_station', fallback=125)
        except ValueError:
            print("[Configuration] The range_of_base_station is not valid.")
            sys.exit(1)

        try:
            self.number_of_uavs_per_base_station = config.getint('UAV', 'number_of_uavs_per_base_station', fallback=2)
        except ValueError:
            print("[Configuration] The number_of_uavs_per_base_station is not valid.")
            sys.exit(1)
        # ... UAV
        try:
            self.max_charge = config.getint('UAV', 'max_charge', fallback=1000)
        except ValueError:
            print("[Configuration] The max_charge is not valid.")
            sys.exit(1)

        try:
            self.battery_low = config.getint('UAV', 'battery_low', fallback=500)
        except ValueError:
            print("[Configuration] The battery_low is not valid.")
            sys.exit(1)

        try:
            self.battery_decrease_per_step = config.getint('UAV', 'battery_decrease_per_step', fallback=1)
        except ValueError:
            print("[Configuration] The battery_decrease_per_step is not valid.")
            sys.exit(1)

        try:
            self.battery_increase_per_step = config.getint('UAV', 'battery_increase_per_step', fallback=10)
        except ValueError:
            print("[Configuration] The battery_increase_per_step is not valid.")
            sys.exit(1)

        try:
            self.sensor_range = config.getint('UAV', 'sensor_range', fallback=5)
        except ValueError:
            print("[Configuration] The sensor_range is not valid.")
            sys.exit(1)

        # Counter for number of steps
        self.steps = 0

        # Store the agent that should be send to the client for more details
        self.details_for = None

        # Create the StaticGrid that contains the landscape (obstacles, base stations, ...)
        self.landscape = StaticGrid(self.width, self.height, background)

        # Add data collector
        self.datacollector = DataCollector(
            {
                "UAVS": lambda m: m.schedule.get_type_count(Uav),
                "Items (Waiting)": self.compute_number_of_items,
                "Items (Picked up)": self.compute_number_of_picked_up_items,
                "Items (Delivered)": self.compute_number_of_delivered_items,
                "Average Delivery Walk Length": self.compute_average_walk_length,
                "Standard Deviation of Average Walk Lengths": self.compute_standard_deviation_walk_lengths,
                "Walklength Divided by Distance": self.compute_walk_length_divided_by_distance,
                "Average lifetime of item": self.compute_item_average_lifetime,
             }
        )

        # In the beginning there are no delivered Items
        self.number_of_delivered_items = 0

        try:
            # Populate the grid with obstacles and BaseStations and UAVs
            self.populate_grid()
        except RuntimeError as error:
            print(error)
            sys.exit(1)

        self.running = True

    def step(self):
        """
        Advance the model one step
        """
        print("Step {}".format(self.steps))
        self.schedule.step()
        # Increase number of steps
        self.steps += 1
        self.item_schedule.step()
        self.datacollector.collect(self)
        dataframe = self.datacollector.get_model_vars_dataframe()
        dataframe.to_csv('out.csv')

    def populate_grid(self):
        """
        Populate the grid with obstacles, BaseStations and UAVs
        """

        # Populate the background with static obstacles
        self.landscape.populate_grid()

        image = Image.new("RGBA", (self.width, self.height))
        for x in range(0, self.width):
            for y in range(0, self.height):
                image.putpixel((x, self.height - y - 1), self.landscape.get_obstacle_color((x, y)))
            image.putpixel((x, self.height - 1), self.landscape.get_obstacle_color((x, 1)))

        file_name = self.background_image_source.split("/").pop()
        new_file_name = file_name.split(".")
        new_file_name.pop()
        new_file_name = new_file_name.pop()
        new_file_name += "_obstacles.png"
        image.save("./delivery/visualization/images/" + new_file_name)
        print("Obstacles done")

        # Create base stations
        base_stations = self.create_base_stations()
        print("BaseStations done")

        # Create UAVs
        uid = 0
        for base_station in base_stations:
            for i in range(self.number_of_uavs_per_base_station):
                uid += 1
                self.create_uav(uid, base_station)
        print("UAVs done")

    def create_base_stations(self):
        """
        Calculate how many BaseStations need to be created and create them
        :returns A list of BaseStations
        """
        base_stations = []
        width = 2 * self.range_of_base_station
        height = 2 * self.range_of_base_station
        number_of_base_stations = int((self.width * self.height) / (width * height))
        x = width
        y = height
        for i in range(0, number_of_base_stations):
            base_stations.append(self.create_base_station(i, (round(x - self.range_of_base_station), round(y - self.range_of_base_station))))
            if x + width > self.width:
                y += height
                x = width
            else:
                x += width
        return base_stations

    def create_base_station(self, bid, pos):
        """
        Create a BaseStation at a given position or close to it
        :param bid: unique identifier of the BaseStation
        :param pos: Tuple of coordinates
        :return The created BaseStation
        """
        x, y = pos
        # Store available cells
        available_cells = set()
        # To check if the coordinates are already stored
        available_cells_helper = set()
        radius = 1
        # If the center is an empty cell
        while not available_cells:
            # ... get neighboring cells and center cell
            neighborhood = self.landscape.get_neighborhood(pos, True, radius)

            # ... search the neighborhood and center
            for coordinates in neighborhood:
                # ... check if there is an obstacle
                for altitude in range(self.max_altitude, 0, -1):
                    if self.landscape.is_obstacle_at_exact(coordinates, altitude):
                        if coordinates not in available_cells_helper:
                            # ... and add the cell to the list of available cells if at least one neighboring cell is not
                            # filled with an obstacle
                            temp_neighborhood = self.landscape.get_neighborhood(coordinates, False, 1)
                            for temp_coordinates in temp_neighborhood:
                                if not self.landscape.is_obstacle_at_exact(temp_coordinates, altitude):
                                    available_cells.add(coordinates + (altitude,))
                                    available_cells_helper.add(coordinates)

            # Increase the search radius if there are no possible cells
            radius += 1

            if radius > self.range_of_base_station:
                raise RuntimeError(
                    'There is no obstacle that fulfills the requirement to be a valid location for a base '
                    'station. A base station needs to be place on top of an obstacle and has to have at least '
                    'one neighboring cell that is not occupied by an obstacle.')

        # If there are available cells, choose one at random
        pos_x, pos_y, pos_z = random.sample(available_cells, 1)[0]

        # Create the BaseStation
        base_station = BaseStation(model=self, pos=(pos_x, pos_y, pos_z), bid=bid, center=(x, y),
                                   range_of_base_station=self.range_of_base_station)
        # Place the BaseStation on the landscape
        self.landscape.place_base_station((pos_x, pos_y))
        # Add the BaseStation to the schedule
        self.schedule.add(base_station)
        return base_station

    def create_uav(self, uid, base_station):
        """
        Create a UAV
        :param uid: unique identifier of the Uav
        :param base_station: the assigned BaseStation
        """
        pos_x, pos_y, pos_z = base_station.get_pos()
        # Create the UAV
        position = (pos_x, pos_y, pos_z)
        uav = Uav(self, pos=position, uid=uid, max_charge=self.max_charge, battery_low=self.battery_low,
                  base_station=base_station, battery_decrease_per_step=self.battery_decrease_per_step,
                  battery_increase_per_step=self.battery_increase_per_step, max_altitude=self.max_altitude,
                  sensor_range=self.sensor_range)
        # Add the UAV to the schedule
        self.schedule.add(uav)

    def get_details_for(self, pos):
        """
        Pick an agent based on the position
        :param pos: Tuple of coordinates (not normalized)
        :return: An agent, if there is an agent at the requested position. Otherwise, None
        """
        pos_x, pos_y = pos
        pos_x = math.floor(pos_x / self.pixel_ratio)
        pos_y = math.floor(pos_y / self.pixel_ratio)
        # Search for BaseStations
        for baseStation in self.schedule.agents_by_type[BaseStation]:
            if pos_x == baseStation.pos[0] and pos_y == baseStation.pos[1]:
                return baseStation
        # Search for UAVs
        for UAV in self.schedule.agents_by_type[Uav]:
            if pos_x == UAV.pos[0] and pos_y == UAV.pos[1]:
                return UAV
        # Search for Items
        for item in self.item_schedule.agents_by_type[Item]:
            if pos_x == item.pos[0] and pos_y == item.pos[1]:
                return item

        return None

    @staticmethod
    def compute_number_of_items(model):
        """
        Compute the number of items that are currently in a base station
        :return: number of items located in all base stations
        """
        number_of_items = 0
        for base_station in model.schedule.agents_by_type[BaseStation]:
            number_of_items += base_station.get_number_of_items()
        return number_of_items

    @staticmethod
    def compute_number_of_picked_up_items(model):
        """
        Compute the number of items that are currently delivered
        :return: number of items that are currently delivered
        """
        number_of_picked_up_items = 0
        for base_station in model.schedule.agents_by_type[BaseStation]:
            number_of_picked_up_items += base_station.get_number_of_items(picked_up=True)
        return number_of_picked_up_items

    @staticmethod
    def compute_number_of_delivered_items(model):
        """
        Computer the number of items that are already delivered
        :param model: The model that the calculation is for
        :return: Number of items that are already delivered
        """
        return model.number_of_delivered_items

    @staticmethod
    def compute_average_walk_length(model):
        """
        Compute the average walk length for all UAVs
        :param model: The model that the calculation is for
        :return: The average walk length
        """
        average_walks = []

        for uav in model.schedule.agents_by_type[Uav]:
            for elem in uav.get_walk_lengths():
                average_walks.append(elem)
        if len(average_walks) > 0:
            return sum(average_walks) / len(average_walks)
        else:
            return 0

    @staticmethod
    def compute_standard_deviation_walk_lengths(model):
        """
        Compute the standard deviation in walk lengths of all UAVs
        :param model: The model that the calculation is for
        :return: The standard deviation of all walk length
        """
        walks = []

        for uav in model.schedule.agents_by_type[Uav]:
            for elem in uav.get_walk_lengths():
                walks.append(elem)
        if len(walks) > 0:
            return np.std(walks)
        else:
            return 0

    @staticmethod
    def compute_walk_length_divided_by_distance(model):
        """
        Compute the ratio between the actual walk and the initial calculated distance
        :param model: The model that the calculation is for
        :return: The ratio between the actual walk and the initial distance
        """
        initial_length_by_distance = []

        for uav in model.schedule.agents_by_type[Uav]:
            for elem in uav.get_walk_length_divided_by_initial_distance():
                initial_length_by_distance.append(elem)
        if len(initial_length_by_distance) > 0:
            return sum(initial_length_by_distance) / len(initial_length_by_distance)
        else:
            return 0

    @staticmethod
    def compute_item_average_lifetime(model):
        """
        Compute the average lifetime of an Item
        :param model: The model that the calculation is for
        :return: The average lifetime of an Item
        """
        result = 0
        if not model.item_schedule.agents_by_type[Item] == []:
            for item in model.item_schedule.agents:
                result = result + item.lifetime
            return result / len(model.item_schedule.agents)
        else:
            return 0
Ejemplo n.º 24
0
class DCGame(Model):
    def __init__(self, adjMat, visibles, adversaries):
        self.adjMat = adjMat  #matrix that keeps track of all players and their neighbors
        self.schedule = SimultaneousActivation(
            self
        )  # An activation in which players' states are effectively updated simultaneously as opposed to sequentially
        self.numAgents = len(adjMat)
        self.terminate = False  # if all non-adversarial players have reached consensus, terminal state is achieved
        self.time = 0
        # logging information
        self.log = Log()

        # total number of color changes in a game
        self.colorChanges = 0

        # convert adjMat to adjList
        def getAdjList(adjMat):
            adjList = {key: [] for key in range(self.numAgents)}
            for node in range(self.numAgents):
                #adjList[node] = [idx for idx, value in enumerate(adjMat[node]) if value == True]
                adjList[node] = [
                    idx for idx, value in enumerate(adjMat[node])
                    if value == 'True'
                ]
            return adjList

        self.adjList = getAdjList(self.adjMat)

        ############# designate visible, adversarial, and consensus nodes #############
        self.visibleColorNodes = visibles
        self.adversarialNodes = adversaries
        self.consensusNodes = [
            n for n in range(self.numAgents) if n not in self.adversarialNodes
        ]

        # adversarial nodes and regular nodes should not overlap
        assert set(self.adversarialNodes) & set(self.consensusNodes) == set()
        # visible nodes should belong to regular nodes
        assert set(self.visibleColorNodes) & set(self.consensusNodes) == set(
            self.visibleColorNodes)

        # logging simulation configuration
        self.log.add("#visible nodes: " + str(self.visibleColorNodes))
        self.log.add("#adversarial nodes: " + str(self.adversarialNodes))
        self.log.add("#consensus nodes: " + str(self.consensusNodes) + '\n')

        ############# initialize all agents #############
        for i in range(self.numAgents):
            # if i is a visible node
            isVisibleNode = i in self.visibleColorNodes
            # if i is an adversarial
            isAdversarial = i in self.adversarialNodes
            # make sure adversarial nodes are not intersected with visible nodes
            assert isVisibleNode & isAdversarial == False

            neighbors = self.adjList[i]

            # visible color nodes in i's neighbors
            vNode = list(set(neighbors) & set(self.visibleColorNodes))

            a = GameAgent(i, isVisibleNode, isAdversarial, neighbors, vNode,
                          self)
            self.schedule.add(a)

        # instantiate all nodes' neighbors and visibleColorNodes
        for agent in self.schedule.agents:
            agent.instantiateNeighbors(self)
            agent.instantiateVisibleColorNodes(self)

        self.datacollector = DataCollector(
            model_reporters={
                "red": getRed,
                "green": getGreen
            },
            agent_reporters={"agent_color": lambda a: a.color})

    # simulate the whole model for one step
    def step(self):
        # # # if either red or green reaches consensus, terminates!
        # # in terminal state we do not collect data
        if not self.terminate:
            self.datacollector.collect(self)
            self.schedule.step()
        return self.terminate

    def simulate(self, simulationTimes):
        for i in range(simulationTimes):
            self.updateTime(i)  # update model's time
            terminate = self.step()
            if terminate:
                break

        #added by Yifan
        hasWhitePlayers = False
        if not terminate:
            # if consensus was not reached in the simulation
            for agent in self.schedule.agents:
                if not agent.isAdversarial and not agent.isVisibleNode and agent.color == "white":
                    #at least one consensus player remained white
                    hasWhitePlayers = True

        # output log file to disk
        self.log.outputLog('result/simResult.txt')
        simulatedResult = self.datacollector.get_model_vars_dataframe()
        return (simulatedResult, hasWhitePlayers)

    # update model's clock
    def updateTime(self, t):
        self.time = t

    def setTerminal(self):
        assert self.terminate == False
        self.terminate = True

    def addRecord(self, msg):
        self.log.add(msg)

    # for degub purpose only
    def outputAdjMat(self, path):
        with open(path, 'w') as fid:
            for line in self.adjMat:
                # convert list of boolean values to string values
                tline = ["1" if item else "0" for item in line]
                fid.write(' '.join(tline) + '\n')