Search code examples
pythonsimulationagent-based-modelingsimpyevent-simulation

Can I use simpy to do agent-based simulation?


I know that simpy is a process-based discrete-event simulation framework. I have several agents that might do different things at the same time. Can I still use simpy to simulate my agents? For example, a car arrives to a parking lot. Then the driver exists. Walks to the entrance of a shopping mall and enters. Some shoppers might leave at the same time. There can be several cars arriving to the parking lot at the same time as well. When an event triggers, that event uses env.timeout(time) time to execute. However, this increases the simulation clock for the environment. Thus, for all agents?? Is there a way to simulate such a case? Could you suggest me a simulation framework/library to simulate such a scenario?

Edit due to @Michael's answer: I do launch the agents (2 type of agents) by using env.process. A While loop function inside of these processes yield env.timeout for agent creation and also creates new processes with env.process(foo(A,B,C)). Inside of these processes, I have several yield env.process. Inside of them again there are some yield env.process. I am using yield on the later functions because agents have to wait for the event to be finished. All of the agents are using some shared resources and stores or waiting for some agents to arrive. There are some associations between different types of agents as well. So, I am using external dictionaries to store all of the information about agents, resources and stores. I had some interesting results that made me think of whether I am using correct library or code structure to achieve what I want to do. After reading @Michael's answer, I believe I am on the right track. I can launch several agent creation processes and they can work simultaneously.


Solution

  • Yes

    env.timeout does not advance the clock. when a agent calls env.timeout, that agent waits until the clock gets to that time, it does not block other agents from doing stuff. Other agents can still do stuff while the first agent is waiting to env.timeout to finish.

    note that each agent needs to be launched asynchronously with env.process, not with yield

    here is a less simple example

    """
    agents calling each other in a agent base sim
    
    Programmer Michael R. Gibbs
    """
    
    import simpy
    import random
    
    class Agent():
        """
        Agents do a background activity, and from time to time make a call to a friend agent
        Agents wait to finish doing stuff, then call back the calling friend agent
        """
    
        def __init__(self, env, agent_id):
            """
            set up agent
    
            Need to set the friend later since that agent may not have been created yet
            """
    
            self.env = env
            self.agent_id = agent_id
            self.friend = None          # set this before starting sim
            self.friend_called = None
    
            # kick off background activity, and activty that makes calls from time to time
            env.process(self.agent_stuff())
            env.process(self.make_call())
        
        def receive_call(self, agent):
            """
            agent is busy, will call back later
            """
    
            # save caller for later callback
            self.friend_called = agent
            print(f"{self.env.now} agent {self.agent_id} just got a call from agent {agent.agent_id}")
    
        def receive_callback(self, agent):
            """
            getting a call from the agent this agent called ealier
            """
            print(f"{self.env.now} agent {self.agent_id} just got a callback from agent {agent.agent_id}")
    
        def make_call(self):
            """
            loop making calls from time to time
            """
            while True:
                yield self.env.timeout(random.randint(4,9))
                self.friend.receive_call(self)
                print(f"{self.env.now} agent {self.agent_id} just made a call to agent {self.friend.agent_id}")
    
        def agent_stuff(self):
            """
            agent process doing agent stuff
            """
            while True:
                t = random.randint(1,5)
                print(f"{self.env.now} agent {self.agent_id} will do his thing in {t}")
                yield env.timeout(t)
                print(f"{self.env.now} agent {self.agent_id} is doing his thing")
    
                if self.friend_called is not None:
                    # oh look, someone called, call them back
                    self.friend_called.receive_callback(self)
                    print(f"{self.env.now} agent {self.agent_id} just made a call to agent {self.friend.agent_id}")
                    self.friend_called = None
    
    env = simpy.Environment()
    
    # create agents, which also starts their processes
    agent1 = Agent(env,1)
    agent2 = Agent(env,2)
    
    # set the friend
    agent1.friend = agent2
    agent2.friend = agent1
    
    env.run(100)