***CONTrOL*** *is an agent-based model developed for the purpose of studying behavioral mechanisms influencing social learning and performance within and across business organisations. CONTrOL stands for Complex Organisational and Network-driven Transmissions resulting in Organisation Learning.* # Model module ```{eval-rst} .. automodule:: model :members: :undoc-members: :show-inheritance: ``` This module involves the creation of the agent-based model and contains the instance initialisation for two classes: * `SocialGraph`, and * `PCSModel` ## Library and module imports ```python import agentpy as ap import networkx as nx import matplotlib.pyplot as plt from organisation.CEO import CEO from landscape.multiplex import MultiplexLandscape ``` ## Social graph instance creation The `SocialGraph` class provides a basic framework for creating and working with a social network graph.. It has a constructor method `__init__` that initializes an empty undirected graph using (`nx.Graph()`). This graph is used to represent the social connections between individuals. ```python class SocialGraph: def __init__(self): self.network = nx.Graph() ``` ## PCS Agent-based model instance creation ### Setup functions The `PCSModel` class inherits from AgentPy in the form of `ap.Model`. It provides a framework for modeling a system that involves multiple agents, a social network, and a landscape. The `setup` function is used to create agent-based `PCSModel` class instances. * It fetches a landscape using the `self.fetchLandscape()` method, essentially calling `MultiplexLandscape`. * It initializes an empty list called organisations. * It creates a social graph using the `SocialGraph` class for modeling social connections between agents, and the resulting network is stored in the `self.socialGraph` attribute. * It creates a list of CEOs using the `ap.AgentList` class, where the number of CEOs is determined by `self.p.numCEOs`, and each CEO is an instance of the `CEO` class. * It initializes the `self.organisationRankingTimestep` attribute to 0, which refers to a later defined function used by CEO's for benchmarking purposes. ```python class PCSModel(ap.Model): def setup(self): self.landscape = self.fetchLandscape() self.organisations = list() self.socialGraph = SocialGraph().network self.CEOs = ap.AgentList(self, self.p.numCEOs, CEO) self.organisationRankingTimestep = 0 def fetchLandscape(self): return MultiplexLandscape(self.p.landscape) ``` ### Timestep definition (simulation execution) The ABM's `step` method orchestrates the progression of the model by defining actions that the several agents perform upon each timestep: * It sets the `self.debugFriendship` attribute to False which can be used for debugging `Individual.socialise()` * It calls the `everyoneSocialise()` method, which essentially executes the `socialise` function that is defined in the [`Individual` class](#soc). * It cascades the step method down through different groups of agents: * First, it calls the step method for all employees managed by managers who are in the CEO group. * Next, it calls the step method for all managers who are part of the CEO group. * Finally, it calls the step method for the CEO group itself. ```python class PCSModel(ap.Model): CONTINUED def step(self): self.debugFriendship = False self.everyoneSocialise() self.CEOs.managers.employees.step() self.CEOs.managers.step() self.CEOs.step() def everyoneSocialise(self): if self.t == 1: self.CEOs.socialise() self.CEOs.managers.socialise() self.CEOs.managers.employees.socialise() ``` The `verifySocialGraph` method is used for visualising the social graph, inspecting the social connections (edges) between agents, and printing agent information if debugging is enabled. * It retrieves the colors of the edges in the `self.socialGraph` and stores them in the `edge_colors` variable using `nx.get_edge_attributes`. * It uses `nx.draw_networkx` to draw the `self.socialGraph` with the specified edge colors. This function visualises the social network using the NetworkX library's graph visualisation capabilities. * If the `self.debugFriendship` attribute is set to True, it calls the `printAgentsIdsAndTypes` method, which prints information about the agents, including their IDs and types. * Finally, it displays the graph and any printed agent information using `plt.show()`, showing the social network visualisation and the agent details if applicable. ```python class PCSModel(ap.Model): CONTINUED def verifySocialGraph(self): edge_colors = nx.get_edge_attributes(self.socialGraph, 'color').values() nx.draw_networkx(self.socialGraph, edge_color=edge_colors) if self.debugFriendship: self.printAgentsIdsAndTypes() plt.show() ``` The `requestOrganisationRanking` is a function to make a ranking of the absolute average fitness performance of each organisation in the landscape, and for CEO's to assess which competitors are outperforming them. * It checks if the current time step `self.t` is different from the `organisationRankingTimestep`. If they are not equal, it proceeds with the ranking process. This check ensures that ranking is performed only once per time step. * If ranking is required, it updates the `organisationRankingTimestep` to the current time step, indicating that ranking has been performed for this time step. * It creates a copy of the list of organisations in the `organisation_list` variable. * It sorts the `organisation_list` based on the average absolute fitness of organisations over a evaluation window as set in the [main module](#main.py). The `getAverageAbsoluteFitness` method is called for each organisation in the list, and the organisations are sorted in descending order, meaning that the best-performing organisations come first. * It then goes through the sorted `organisation_list` and for each organisation, it initializes an empty list `org.competitorsPerformingBetter`. * If the organisation is not the best-performing organisation (i.e., i > 0), it adds the organisation immediately preceding it in the sorted list (`organisation_list[i-1]`) to its `competitorsPerformingBetter` list. * For subsequent competitors (if `i - j > 0`), where `j` ranges from 2 to `org.competitorsToConsider`, additional competitors are added to the `org.competitorsPerformingBetter` list. ```python class PCSModel(ap.Model): CONTINUED def requestOrganisationRanking(self): if self.organisationRankingTimestep != self.t: self.organisationRankingTimestep = self.t organisation_list = self.organisations.copy() organisation_list.sort(key=lambda org: org.getAverageAbsoluteFitness(self.t - self.p.mcsEvaluationWindow, self.t), reverse=True) for i, org in enumerate(organisation_list): org.competitorsPerformingBetter = [] if i > 0: org.competitorsPerformingBetter.append(organisation_list[i-1]) for j in range(2,org.competitorsToConsider+1): if (i-j) > 0: org.competitorsPerformingBetter.append(organisation_list[i-1]) ```