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

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

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.

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.

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.

  • 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.

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.

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. 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.

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])