-3

Hi everyone i've produced an agent based model. The agents can move randomly and use the A* algorithm to move to places to places. There are two main collections 1) arrival Queue (arraylist) and the 2) main agents (arraylist). All the agents are initially placed into the arrival queue, agents are placed into the simulation at certain time intervals e.g. 100 agents per 400 simulation steps).

The issue im getting is every time I increase the population of agents from 3000+ the simulation begins to lag. Also im using the Java 2D libraries for visualizing the 2D array of agents. Bellow ive added a screenshot of the the visualVM of my application is shows the methods which are being called the most (CPU time).

ScreenShot Here

Model code:

package Model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;




import Model.Grid;
import Agent.*;

public class Model extends AbstractModel implements Runnable{

    private Grid grid;
    private Constants modelConstants;
    private RandomGenerator randGenerator;
    private GridMapLayout Maplayout;
    private RoomController roomController;
    private AgentFactory agentFacory;
    private Statistics stats;

    private ArrayList<AbstractAgent> agents;
    private Queue<Patient> patientArrivalQueue;
    private List<Patient> dischargedPatients;
    private Queue<Doctor> doctorsQueue;
    private Queue<Nurse> nursesQueue;

    private int patientPopulation;
    private int doctorPopulation;
    private int receptionNurses;
    private int aNeNurses;
    private int ward1Nurses;
    private int ward2Nurses;
    private int ward3Nurses;

    private boolean running;
    private int currentSimCycle;
    private int simArrivalCounter;

    public CTimer timer = new CTimer();

    public Model() {
        super();
        this.grid = new Grid(150, 150); //if no given size grid uses default size 
        this.modelConstants = new Constants();
        this.randGenerator = new RandomGenerator(modelConstants.getSEED());
        this.Maplayout = new GridMapLayout(this);
        this.roomController = new RoomController(this);
        this.agentFacory = new AgentFactory(this);
        this.stats = new Statistics(this);
        this.currentSimCycle = 0;
        this.simArrivalCounter = 0;

        this.agents = new ArrayList<AbstractAgent>();
        this.patientArrivalQueue = new LinkedList<Patient>();
        this.dischargedPatients = new LinkedList<Patient>();
        this.doctorsQueue = new LinkedList<Doctor>();
        this.nursesQueue =  new LinkedList<Nurse>();

        loadModelConstants();
        loadMapLayout();
        roomController.readyRoomCollections();
        createAllAgents();
        loadDoctorsInRooms();
        loadNursesInRooms();
        loadNextPatientArrival();
//      randomlyPopulateGrid();
        stats.updateStatistics();
        stats.setDataValid(false);

//      Room entrance = roomController.getAllRooms().get("Entrance");
//      Room reception = roomController.getAllRooms().get("Reception");
//      Room aNe = roomController.getAllRooms().get("A&E");
//      Room ward1 = roomController.getAllRooms().get("Ward 1");
//      Room ward2 = roomController.getAllRooms().get("Ward 2");
//      Room ward3 = roomController.getAllRooms().get("Ward 3");
//      Room canteen = roomController.getAllRooms().get("Canteen");
//      Room toilet = roomController.getAllRooms().get("Toilet");
//      
//      System.out.println(toilet);

//      System.out.println(entrance.getDoorLocations().size());
//      System.out.println("Reception: " + reception.getRoomLocations().size());
//      System.out.println("A&E: " + aNe.getRoomLocations().size());
//      System.out.println("Ward 1: " + ward1.getRoomLocations().size());
//      System.out.println("Ward 2: " + ward2.getRoomLocations().size());
//      System.out.println("Ward 3: " + ward3.getRoomLocations().size());
//      System.out.println("Canteen: " + canteen.getRoomLocations().size());
//      System.out.println("Toilet: " + toilet.getRoomLocations().size());
    }

    public void loadModelConstants() {
        this.patientPopulation = modelConstants.getPatientPopulation();
        this.doctorPopulation = modelConstants.getDoctorPopulation();
        this.receptionNurses = modelConstants.getReceptionNursePopulation();
        this.aNeNurses =  modelConstants.getaNeNursePopulation();
        this.ward1Nurses =  modelConstants.getWard1NursePopulation();
        this.ward2Nurses =  modelConstants.getWard2NursePopulation();
        this.ward3Nurses =  modelConstants.getWard3NursePopulation();

//      System.out.println(this.patientPopulation);
//      System.out.println(this.doctorPopuation);
//      System.out.println(this.nursePopulation);
        System.out.println("Loaded Model Constants");
    }

    public void loadMapLayout() {
        Maplayout.loadMapImage();
        Maplayout.loadRGBfileAndRooms();
        Maplayout.readPixels();
        Maplayout.addMapLayout();
//      layout.printCells();
        System.out.println("Loaded Map Layout");
    }

    public void createAllAgents() {
        ArrayList<Patient> tempPatient = new ArrayList<Patient>();
        ArrayList<Doctor> tempDoctor = new ArrayList<Doctor>();
        ArrayList<Nurse> tempNurse = new ArrayList<Nurse>();

        int totalNurses = + (modelConstants.getReceptionNursePopulation()
                          +  modelConstants.getaNeNursePopulation()
                          +  modelConstants.getWard1NursePopulation()
                          +  modelConstants.getWard2NursePopulation()
                          +  modelConstants.getWard3NursePopulation()
                            );

        int noOfAgentLeftToCreate = modelConstants.getPatientPopulation() + modelConstants.getDoctorPopulation() 
                                  + totalNurses;

        while(tempPatient.size() != this.patientPopulation) {
            Patient newPatient = (Patient) this.agentFacory.createAgent("PATIENT", noOfAgentLeftToCreate);
            tempPatient.add(newPatient);
            noOfAgentLeftToCreate--;
        }

        while(tempDoctor.size() != this.doctorPopulation) {
            Doctor newDoctor = (Doctor)this.agentFacory.createAgent("DOCTOR", noOfAgentLeftToCreate);
            tempDoctor.add(newDoctor);
            noOfAgentLeftToCreate--;
        }

        while(tempNurse.size() != totalNurses) {
            Nurse newNurse = (Nurse)this.agentFacory.createAgent("NURSE", noOfAgentLeftToCreate);
            tempNurse.add(newNurse);
            noOfAgentLeftToCreate--;
        }

        this.patientArrivalQueue.addAll(tempPatient); //Add total patients to arrive queue 
        this.doctorsQueue.addAll(tempDoctor); //Add all doctors to the doctors agent collection
        this.nursesQueue.addAll(tempNurse); //Add all nurses to the nurse agent collection
        System.out.println("All agents created");
    }

    public void loadDoctorsInRooms() {
        for(Map.Entry<String, Room> pair : roomController.getDoctorRooms().entrySet()) {
            String key = pair.getKey();
            Room value = pair.getValue();

            if(value.isRoomEmpty()) {
                Doctor newDoctor = doctorsQueue.remove();
                Location randFreeRoomLoc = value.getRandomFreeRoomLocation();
                grid.moveAgentToNewLocation(newDoctor, newDoctor.getLocation(), randFreeRoomLoc);
                newDoctor.setLocation(randFreeRoomLoc);

                newDoctor.setAllocatedRoom(value);
                newDoctor.getAgentController().setCurrentRoom(value);
                newDoctor.getAgentController().setTargetRoom(value);
                //Tell the room that this agent is now a occupant of the room
                value.getCurrentOccupantsMap().put(newDoctor.getAgentID(), newDoctor);
                agents.add(newDoctor);
            }
        }
        Room aNeRoom = roomController.getAllRooms().get("A&E");
        if(aNeRoom.isRoomEmpty()) {
            Doctor aNeDoc = doctorsQueue.remove();
            Location randFreeRoomLoc = aNeRoom.getRandomFreeRoomLocation();
            grid.moveAgentToNewLocation(aNeDoc, aNeDoc.getLocation(), randFreeRoomLoc);
            aNeDoc.setLocation(randFreeRoomLoc);

            aNeDoc.setAllocatedRoom(aNeRoom);
            aNeDoc.getAgentController().setCurrentRoom(aNeRoom);
            aNeDoc.getAgentController().setTargetRoom(aNeRoom);
            //Tell the room that this agent is now a occupant of the room
            aNeRoom.getCurrentOccupantsMap().put(aNeDoc.getAgentID(), aNeDoc);
            agents.add(aNeDoc);
        }

        System.out.println("All Doctors are placed");
    }

    public void loadNursesInRooms() {
        HashMap<String, Room> nurseRooms = new HashMap<String, Room>();
        nurseRooms.putAll( roomController.getAllRooms());
        nurseRooms.remove("Toilet");
        nurseRooms.remove("Canteen");
        nurseRooms.remove("Staff");
        nurseRooms.remove("Corridor");
        nurseRooms.remove("ISOL Entrance");
        nurseRooms.remove("Entrance");
        nurseRooms.remove("Exit");

        for(Map.Entry<String, Room> drPair :  roomController.getDoctorRooms().entrySet()) {
            String drKey = drPair.getKey();
            Room drValue = drPair.getValue();
            for(Map.Entry<String, Room> isolPair :  roomController.getIsolRooms().entrySet()) {
                String isolKey = isolPair.getKey();
                Room isolValue = isolPair.getValue();

                if(nurseRooms.containsKey(drKey)) {
                    nurseRooms.remove(drKey);
                }
                if(nurseRooms.containsKey(isolKey)) {
                    nurseRooms.remove(isolKey);
                }
            }
        }

        for(Map.Entry<String, Room> pair : nurseRooms.entrySet()) {
            String key = pair.getKey();
            Room value = pair.getValue();

            int noOfNurseToCreate = 0;

            if(key.contentEquals("Reception")) {
                noOfNurseToCreate = modelConstants.getReceptionNursePopulation();
            }
            else if(key.contentEquals("A&E")) {
                noOfNurseToCreate = modelConstants.getaNeNursePopulation();
            }
            else if(key.contentEquals("Ward 1")) {
                noOfNurseToCreate = modelConstants.getWard1NursePopulation();
            }
            else if(key.contentEquals("Ward 2")) {
                noOfNurseToCreate = modelConstants.getWard2NursePopulation();
            }
            else if(key.contentEquals("Ward 3")) {
                noOfNurseToCreate = modelConstants.getWard3NursePopulation();
            }

            for(int i=0; i != noOfNurseToCreate; i++) {
                Nurse newNurse = nursesQueue.remove();
                Location roomLocation = value.getRandomFreeRoomLocation();
                grid.moveAgentToNewLocation(newNurse, newNurse.getLocation(), roomLocation);
                newNurse.setLocation(roomLocation);

                newNurse.setAllocatedRoom(value);
                newNurse.getAgentController().setCurrentRoom(value);
                newNurse.getAgentController().setTargetRoom(value);
                //Tell the room controller that this agent is now an occupant of the room
                value.getCurrentOccupantsMap().put(newNurse.getAgentID(), newNurse);
                agents.add(newNurse);
            }
        }

        System.out.println("All Nurses are placed");
    }

    public void loadNextPatientArrival() {
        if(this.simArrivalCounter == 0) {
            int removeThemPatients = 0;
            //If there are more patients in the arrival queue then the specified simPatientarrival rate then 
            //remove the specified amount of patients per simPatient arrival rate 
            if(patientArrivalQueue.size() >= modelConstants.getPatientArrivalRate()) { 
                removeThemPatients = modelConstants.getPatientArrivalRate();
            }
            //Number of patient to place on the grid is less then the patient arrival rate, just place the rest into the entrance
            else {
                removeThemPatients = patientArrivalQueue.size();
            }

            Room reception = roomController.getAllRooms().get("Reception");
            Room aNe = roomController.getAllRooms().get("A&E");

            for(int i=removeThemPatients; i !=0; i--) { 
                Patient nextPatient = patientArrivalQueue.remove();
//              if(nextPatient.getPatientType().contains("Normal") & nextPatient.getLocation() != null) {
                if(grid.checkEmptyAt(nextPatient.getLocation())) {
                    grid.placeAgent(nextPatient, nextPatient.getLocation());
                    agents.add(nextPatient);
                }
                else {
                     patientArrivalQueue.add(nextPatient);
                }
//              }
            }

            //IMPORTANT need to reset the simArrivalCounter back to the specified count
            this.simArrivalCounter = this.modelConstants.getSimArrivalCounter();
            //              System.out.println("More patients have arrived");
        }
    }

    public void removeDischargedPatients() {
        //Just iterate through the exit room door locations and remove 
        //the the discharged patients waiting there into the patient discharge list
        Room exit = roomController.getAllRooms().get("Exit");
        for(Location loc : exit.getDoorLocations()) {
            if(!grid.checkEmptyAt(loc)) {
                Patient dischargedPatient = (Patient) grid.getAgentAtLocation(loc);
                grid.clearLocationAt(loc);
                agents.remove(dischargedPatient);
                dischargedPatients.add(dischargedPatient);
            }
        }
    }

    public void randomlyPopulateGrid() {
        Room reception =  roomController.getAllRooms().get("Reception");
        while(!this.patientArrivalQueue.isEmpty()) {
            Location freeLoc = reception.getRandomFreeRoomLocation();
            Patient nextPatient = this.patientArrivalQueue.remove();
            grid.moveAgentToNewLocation(nextPatient, nextPatient.getLocation(), freeLoc);
            nextPatient.setLocation(freeLoc);

            nextPatient.getAgentController().setCurrentRoom(reception);
            nextPatient.getAgentController().setTargetRoom(reception);
            agents.add(nextPatient);
        }

        System.out.println("Populate Grid");
    }

    public void simulateCycles(int maxCycles) {
        for(int i=0; i != maxCycles; i++) {
            loadNextPatientArrival();
            removeDischargedPatients();
            updateAgents();
            stats.updateStatistics();
            stats.setDataValid(false);


            this.currentSimCycle++;
            this.simArrivalCounter--;
        }

        notifyViews();
    }

    public void updateAgents() {
//      Iterator<AbstractAgent> it = agents.iterator();
//      while(it.hasNext()) {
//          AbstractAgent nextAgent = it.next();
//          nextAgent.act();
//      }
        for(AbstractAgent agent : agents) {
            agent.act();
        }
    }

    @Override
    public void startSimulation() {
        new Thread(this).start();
        System.out.println("Running Simulation");
    }

    @Override
    public void stopSimulation() {
        running = false;
        System.out.println("Simulation has stoped");
    }

    @Override
    public void resetSimulation() {
        System.out.println("Model Constant set to default");
    }

    public void run() {
        running = true;
//      int loop = 0;
        while(running) {
//          System.out.println("loop: "+ loop);
//          loop++;
            simulateCycles(1);
            try {
                Thread.sleep(5);
            } 
            catch (Exception e) {

            }
        }
    }

    public Grid getGrid() {
        return grid;
    }

    public List<AbstractAgent> getAgentsCollection() {
        return agents;
    }

    public GridMapLayout getGridMayLayout() {
        return Maplayout;
    }

    public RoomController getRoomController() {
        return roomController;
    }

    public Constants getModelConstants() {
        return modelConstants;
    }

    public RandomGenerator getRandGenerator() {
        return randGenerator;
    }

    public int getCurrentSimCycle() {
        return currentSimCycle;
    }

    public Statistics getStats() {
        return stats;
    }
}

1 Answers1

1

This is a characteristic of time-stepped agent-based modeling. Since each of n agents can in principle interact with any of the other agents, there are n choose 2 interactions to be evaluated at each time step, which grows as O(n2). Your basic choices are: 1) keep the number of agents small; 2) increase the size of your time steps (which usually introduces modeling errors due to the coarser approximation of what's happening); 3) "localize" the scope of interactions (which may or may not be realistic for what you're modeling); or 4) rewrite your model to use discrete event scheduling rather than time stepping.

pjs
  • 18,696
  • 4
  • 27
  • 56