From ebff1d61c161de98c2286a27520f1af7f7d8adad Mon Sep 17 00:00:00 2001 From: Aman Dhruva Thamminana <48414198+amantham20@users.noreply.github.com> Date: Sun, 3 Dec 2023 02:50:21 -0500 Subject: [PATCH 01/21] added scavenger scheduling --- source/Agents/GP/CGPAgent.hpp | 2 +- source/Agents/GP/GPAgentBase.hpp | 2 +- source/Agents/GP/GPTrainingLoop.hpp | 116 ++++++++++++++++++++++++++-- source/Agents/GP/LGPAgent.hpp | 2 +- source/gp_train_main.cpp | 9 ++- 5 files changed, 119 insertions(+), 12 deletions(-) diff --git a/source/Agents/GP/CGPAgent.hpp b/source/Agents/GP/CGPAgent.hpp index 08378896..9063c554 100644 --- a/source/Agents/GP/CGPAgent.hpp +++ b/source/Agents/GP/CGPAgent.hpp @@ -108,7 +108,7 @@ namespace cowboys { /// @brief Load in the string representation of a genotype and configure this agent based on it. /// @param genotype The string representation of a genotype. - void Import(std::string genotype) { + void Import(const std::string &genotype) override { this->genotype.Configure(genotype); decision_graph = GraphBuilder().CartesianGraph(this->genotype, FUNCTION_SET, this); } diff --git a/source/Agents/GP/GPAgentBase.hpp b/source/Agents/GP/GPAgentBase.hpp index 28128d86..88825ebf 100644 --- a/source/Agents/GP/GPAgentBase.hpp +++ b/source/Agents/GP/GPAgentBase.hpp @@ -71,7 +71,7 @@ namespace cowboys { virtual void Reset(bool /*hard*/ = false) { extra_state["previous_action"] = 0; }; // virtual void crossover(const GPAgentBase &other) {}; - // virtual void Import(const std::string &genotype) {}; + virtual void Import(const std::string &genotype) {}; // -- Random Number Generation -- diff --git a/source/Agents/GP/GPTrainingLoop.hpp b/source/Agents/GP/GPTrainingLoop.hpp index 2ca967da..c8500de7 100644 --- a/source/Agents/GP/GPTrainingLoop.hpp +++ b/source/Agents/GP/GPTrainingLoop.hpp @@ -75,12 +75,15 @@ namespace cowboys { std::vector>> endPositions = std::vector>>(); std::vector>> independentAgentFitness = std::vector>>(); + int global_max_threads = std::thread::hardware_concurrency(); + public: + bool ScavengerQueuing = false; /** * @brief: constructor */ - GPTrainingLoop() { + GPTrainingLoop(const bool scavengerQueuing = false) : ScavengerQueuing(scavengerQueuing) { topAgentsDoc.InsertFirstChild(rootTopAllGenerations); @@ -146,6 +149,74 @@ namespace cowboys { } + if (ScavengerQueuing) + { + + std::filesystem::path normalizedAbsolutePath = getSystemPath(); +// find the file that is latest or max lexographically + std::string lastGenerationsPrefix = "allAgentData_"; + std::string lastGenerationsFilenameExtension = ".xml"; + + std::vector matchingFiles; + for (const auto & entry : std::filesystem::directory_iterator(normalizedAbsolutePath)) + { + if (entry.path().extension() == lastGenerationsFilenameExtension && entry.path().filename().string().find(lastGenerationsPrefix) != std::string::npos) + { + matchingFiles.push_back(entry.path()); + } + } + std::sort(matchingFiles.begin(), matchingFiles.end()); + std::filesystem::path lastFile = matchingFiles.back(); + + std::cout << "Last File: " << lastFile << std::endl; + + allOfLastGeneration.LoadFile(lastFile.string().c_str()); + rootAllOfLastGeneration = allOfLastGeneration.FirstChildElement("GPLoopAllOfLastGeneration"); + + std::cout << "Loaded last file" << std::endl; + +// if (auto agentType = dynamic_cast(&agents[0][0])) { +// std::cout << "Agent Type is CGPAgent" << std::endl; +// agentType->Import(lastFile.string()); +// } +// else if (auto agentType = dynamic_cast(&agents[0][0])) { +// std::cout << "Agent Type is LGPAgent" << std::endl; +// agentType->Import(lastFile.string()); +// } +// else { +// std::cout << "Agent Type is not CGPAgent or LGPAgent" << std::endl; +// } +// use typetraits to check the type of the agent + if (std::is_same::value) { + std::cout << "Agent Type is CGPAgent" << std::endl; + } + else if (std::is_same::value) { + std::cout << "Agent Type is LGPAgent" << std::endl; + assert(false); //TODO: Agent not implemented for import + } + else { + std::cout << "Agent Type is not CGPAgent or LGPAgent" << std::endl; + } + + + auto *latestGenerationElem = rootAllOfLastGeneration->FirstChildElement(); + std::cout << latestGenerationElem->Name() << std::endl; + auto *agentElem = latestGenerationElem->FirstChildElement(); + tinyxml2::XMLElement* generationElem = nullptr; + + std::cout << agentElem->Name() << std::endl; + for (size_t i = 0; i < numArenas; ++i) { + for (size_t j = 0; j < NumAgentsForArena; ++j) { + + generationElem = agentElem->FirstChildElement(); + const char *genotypeData = generationElem->GetText(); + agents.at(i).at(j)->Import(genotypeData); + agentElem = agentElem->NextSiblingElement(); + } + } + + } + Printgrid(STARTPOSITIONS); @@ -234,6 +305,8 @@ namespace cowboys { size_t threadsComplete = 0; + + for (size_t arena = 0; arena < environments.size(); ++arena) { if (maxThreads == 0 || threads.size() < maxThreads) { threads.emplace_back(&GPTrainingLoop::RunArena, this, arena, numberOfTurns); @@ -287,15 +360,47 @@ namespace cowboys { auto startTime = std::chrono::high_resolution_clock::now(); + global_max_threads = maxThreads; + + SaveDataParams saveDataParams(0); saveDataParams.save = saveData; saveDataParams.saveMetaData = true; -// saveDataParams.saveAllAgentData = true; + saveDataParams.saveAllAgentData = true; // // saveDataParams.saveTopAgents = true; // saveDataParams.saveLastGenerations = true; - for (size_t generation = 0; generation < numGenerations; ++generation) { + + +// check to see if meta data exists + std::filesystem::path normalizedAbsolutePath = getSystemPath(); + std::string metaDataFilename = "metaData.xml"; +// check to see if the file exists + std::filesystem::path metaDataFullPath = normalizedAbsolutePath / metaDataFilename; + + + size_t generation = 0; // <- the generation to start at + + if (std::filesystem::exists(metaDataFullPath) && ScavengerQueuing) { + std::cout << "MetaData file exists" << std::endl; + metaData.LoadFile(metaDataFullPath.string().c_str()); + rootMetaData = metaData.FirstChildElement("GPLoopMetaData"); + auto *generationTag = rootMetaData->FirstChildElement(); + generation = generationTag->UnsignedAttribute("generation") + 1; + std::cout << "Starting at generation " << generation << std::endl; + + } else { + if (ScavengerQueuing) + { + std::cout << "MetaData file does not exist Starting a new Scavenger Queue" << std::endl; + } + + rootMetaData = metaData.NewElement("GPLoopMetaData"); + metaData.InsertFirstChild(rootMetaData); + } + + for (; generation < numGenerations; ++generation) { auto generationStartTime = std::chrono::high_resolution_clock::now(); saveDataParams.updateGeneration(generation); @@ -484,7 +589,7 @@ namespace cowboys { std::string tagName = "generation_" + std::to_string(generation); auto *generationTag = metaData.NewElement(tagName.c_str()); - + generationTag->SetAttribute("generation", generation); generationTag->SetAttribute("averageFitness", averageFitness); generationTag->SetAttribute("maxFitness", maxFitness); generationTag->SetAttribute("bestAgentIDX", bestAgent.second); @@ -672,7 +777,8 @@ namespace cowboys { const int MIDDLE_MUTATE_STARTBOUND = int(ELITE_POPULATION_PERCENT * sortedAgents.size()); // Determine the number of threads to use - const int num_threads = std::thread::hardware_concurrency(); + const int num_threads = global_max_threads; +// const int num_threads = std::min(static_cast(std::thread::hardware_concurrency()), global_max_threads); std::vector threads; diff --git a/source/Agents/GP/LGPAgent.hpp b/source/Agents/GP/LGPAgent.hpp index 39ab9c9a..886b3844 100644 --- a/source/Agents/GP/LGPAgent.hpp +++ b/source/Agents/GP/LGPAgent.hpp @@ -247,7 +247,7 @@ namespace cowboys * @brief Export the agent to a string * @return */ - std::string Export() { + std::string Export() override { std::string encodedLists = ""; for (auto instruction : instructionsList) diff --git a/source/gp_train_main.cpp b/source/gp_train_main.cpp index d139071b..47884826 100644 --- a/source/gp_train_main.cpp +++ b/source/gp_train_main.cpp @@ -15,17 +15,18 @@ int main() { const int num_threads = std::thread::hardware_concurrency(); +// const int num_threads = 100; std::cout << "Number of threads: " << num_threads << std::endl; auto start_time = std::chrono::high_resolution_clock::now(); -// for (size_t i = 0; i < 20; ++i){ - cowboys::GPTrainingLoop loop; + + cowboys::GPTrainingLoop loop(true); loop.Initialize(200, 100); - loop.Run(11, 100, num_threads, true); -// } + loop.Run(150, 100, num_threads, true); + auto end_time = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end_time - start_time); From b2f3f1da6af686c15c7e2c39d2cbff754214ff5b Mon Sep 17 00:00:00 2001 From: Aman Dhruva Thamminana <48414198+amantham20@users.noreply.github.com> Date: Sun, 3 Dec 2023 04:22:07 -0500 Subject: [PATCH 02/21] added first load jump --- source/Agents/GP/GPTrainingLoop.hpp | 96 +++++++++++++++++++---------- source/gp_train_main.cpp | 6 +- 2 files changed, 67 insertions(+), 35 deletions(-) diff --git a/source/Agents/GP/GPTrainingLoop.hpp b/source/Agents/GP/GPTrainingLoop.hpp index c8500de7..19dcd04c 100644 --- a/source/Agents/GP/GPTrainingLoop.hpp +++ b/source/Agents/GP/GPTrainingLoop.hpp @@ -77,6 +77,9 @@ namespace cowboys { int global_max_threads = std::thread::hardware_concurrency(); + double ELITE_POPULATION_PERCENT = 0.1; + double UNFIT_POPULATION_PERCENT = 0.2; + public: bool ScavengerQueuing = false; @@ -149,26 +152,33 @@ namespace cowboys { } + loadLastGeneration(); + + Printgrid(STARTPOSITIONS); + + + const size_t numAgents = numArenas * NumAgentsForArena; + + std::stringstream ss; + ss.imbue(std::locale("")); + ss << std::fixed << numAgents; + + std::cout << "number of agents " << std::fixed << ss.str() << std::endl; + + } + + + void loadLastGeneration() { if (ScavengerQueuing) { - std::filesystem::path normalizedAbsolutePath = getSystemPath(); -// find the file that is latest or max lexographically - std::string lastGenerationsPrefix = "allAgentData_"; - std::string lastGenerationsFilenameExtension = ".xml"; + auto lastFile = FullLoadGrabLatestGeneration(); - std::vector matchingFiles; - for (const auto & entry : std::filesystem::directory_iterator(normalizedAbsolutePath)) + if (lastFile == "NOTTAFILE") { - if (entry.path().extension() == lastGenerationsFilenameExtension && entry.path().filename().string().find(lastGenerationsPrefix) != std::string::npos) - { - matchingFiles.push_back(entry.path()); - } + std::cout << "No last file found" << std::endl; + return; } - std::sort(matchingFiles.begin(), matchingFiles.end()); - std::filesystem::path lastFile = matchingFiles.back(); - - std::cout << "Last File: " << lastFile << std::endl; allOfLastGeneration.LoadFile(lastFile.string().c_str()); rootAllOfLastGeneration = allOfLastGeneration.FirstChildElement("GPLoopAllOfLastGeneration"); @@ -187,16 +197,16 @@ namespace cowboys { // std::cout << "Agent Type is not CGPAgent or LGPAgent" << std::endl; // } // use typetraits to check the type of the agent - if (std::is_same::value) { - std::cout << "Agent Type is CGPAgent" << std::endl; - } - else if (std::is_same::value) { - std::cout << "Agent Type is LGPAgent" << std::endl; - assert(false); //TODO: Agent not implemented for import - } - else { - std::cout << "Agent Type is not CGPAgent or LGPAgent" << std::endl; - } + if (std::is_same::value) { + std::cout << "Agent Type is CGPAgent" << std::endl; + } + else if (std::is_same::value) { + std::cout << "Agent Type is LGPAgent" << std::endl; + assert(false); //TODO: Agent not implemented for import + } + else { + std::cout << "Agent Type is not CGPAgent or LGPAgent" << std::endl; + } auto *latestGenerationElem = rootAllOfLastGeneration->FirstChildElement(); @@ -205,6 +215,8 @@ namespace cowboys { tinyxml2::XMLElement* generationElem = nullptr; std::cout << agentElem->Name() << std::endl; + size_t numArenas = agents.size(); + size_t NumAgentsForArena = agents.at(0).size(); for (size_t i = 0; i < numArenas; ++i) { for (size_t j = 0; j < NumAgentsForArena; ++j) { @@ -215,19 +227,37 @@ namespace cowboys { } } + std::cout << "Agents LoadComplete" << std::endl; + } + } - Printgrid(STARTPOSITIONS); + std::filesystem::path FullLoadGrabLatestGeneration() { + std::filesystem::path normalizedAbsolutePath = getSystemPath(); + std::string lastGenerationsPrefix = "allAgentData_"; + std::string lastGenerationsFilenameExtension = ".xml"; - const size_t numAgents = numArenas * NumAgentsForArena; + std::vector matchingFiles; + for (const auto & entry : std::filesystem::directory_iterator(normalizedAbsolutePath)) + { + if (entry.path().extension() == lastGenerationsFilenameExtension && entry.path().filename().string().find(lastGenerationsPrefix) != std::string::npos) + { + matchingFiles.push_back(entry.path()); + } + } + std::sort(matchingFiles.begin(), matchingFiles.end()); - std::stringstream ss; - ss.imbue(std::locale("")); - ss << std::fixed << numAgents; + if (matchingFiles.empty()) + { + return "NOTTAFILE"; + } - std::cout << "number of agents " << std::fixed << ss.str() << std::endl; + std::filesystem::path lastFile = matchingFiles.back(); + + std::cout << "Last File: " << lastFile << std::endl; + return lastFile; } @@ -347,6 +377,8 @@ namespace cowboys { } + + /** * @brief: runs the Genetic Programming training loop for a number of generations to evolve the agents * @@ -756,8 +788,8 @@ namespace cowboys { */ void GpLoopMutateHelper() { - constexpr double ELITE_POPULATION_PERCENT = 0.1; - constexpr double UNFIT_POPULATION_PERCENT = 0.2; +// constexpr double ELITE_POPULATION_PERCENT = 0.1; +// constexpr double UNFIT_POPULATION_PERCENT = 0.2; const int ELITE_POPULATION_SIZE = int(ELITE_POPULATION_PERCENT * sortedAgents.size()); diff --git a/source/gp_train_main.cpp b/source/gp_train_main.cpp index 47884826..fb400916 100644 --- a/source/gp_train_main.cpp +++ b/source/gp_train_main.cpp @@ -15,7 +15,7 @@ int main() { const int num_threads = std::thread::hardware_concurrency(); -// const int num_threads = 100; +// const int num_threads = 12; std::cout << "Number of threads: " << num_threads << std::endl; @@ -24,8 +24,8 @@ int main() { cowboys::GPTrainingLoop loop(true); - loop.Initialize(200, 100); - loop.Run(150, 100, num_threads, true); + loop.Initialize(100, 2000); + loop.Run(150, 50, num_threads, true); auto end_time = std::chrono::high_resolution_clock::now(); From 760fe456a3a298d90ec55343b769e57ae9b60916 Mon Sep 17 00:00:00 2001 From: Aman Dhruva Thamminana <48414198+amantham20@users.noreply.github.com> Date: Sun, 3 Dec 2023 04:37:20 -0500 Subject: [PATCH 03/21] Determinisim for rand --- source/Agents/GP/GPTrainingLoop.hpp | 7 +++++-- source/gp_train_main.cpp | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/source/Agents/GP/GPTrainingLoop.hpp b/source/Agents/GP/GPTrainingLoop.hpp index 19dcd04c..8b40a442 100644 --- a/source/Agents/GP/GPTrainingLoop.hpp +++ b/source/Agents/GP/GPTrainingLoop.hpp @@ -92,7 +92,7 @@ namespace cowboys { rootMetaData = metaData.NewElement("GPLoopMetaData"); metaData.InsertFirstChild(rootMetaData); - + srand(TRAINING_SEED); ResetMainTagLastGenerations(); } @@ -413,13 +413,14 @@ namespace cowboys { size_t generation = 0; // <- the generation to start at - + int tempRandSeed = rand(); if (std::filesystem::exists(metaDataFullPath) && ScavengerQueuing) { std::cout << "MetaData file exists" << std::endl; metaData.LoadFile(metaDataFullPath.string().c_str()); rootMetaData = metaData.FirstChildElement("GPLoopMetaData"); auto *generationTag = rootMetaData->FirstChildElement(); generation = generationTag->UnsignedAttribute("generation") + 1; + tempRandSeed = generationTag->UnsignedAttribute("randSeed"); std::cout << "Starting at generation " << generation << std::endl; } else { @@ -433,6 +434,7 @@ namespace cowboys { } for (; generation < numGenerations; ++generation) { + srand(tempRandSeed); auto generationStartTime = std::chrono::high_resolution_clock::now(); saveDataParams.updateGeneration(generation); @@ -625,6 +627,7 @@ namespace cowboys { generationTag->SetAttribute("averageFitness", averageFitness); generationTag->SetAttribute("maxFitness", maxFitness); generationTag->SetAttribute("bestAgentIDX", bestAgent.second); + generationTag->SetAttribute("Rand", rand()); rootMetaData->InsertFirstChild(generationTag); diff --git a/source/gp_train_main.cpp b/source/gp_train_main.cpp index fb400916..e4897b04 100644 --- a/source/gp_train_main.cpp +++ b/source/gp_train_main.cpp @@ -24,8 +24,8 @@ int main() { cowboys::GPTrainingLoop loop(true); - loop.Initialize(100, 2000); - loop.Run(150, 50, num_threads, true); + loop.Initialize(50, 200); + loop.Run(80, 50, num_threads, true); auto end_time = std::chrono::high_resolution_clock::now(); From 54fb4c1b664a98b2cdd1c81230b20eb7d8ba69f4 Mon Sep 17 00:00:00 2001 From: Aman Dhruva Thamminana <48414198+amantham20@users.noreply.github.com> Date: Sun, 3 Dec 2023 14:39:10 -0500 Subject: [PATCH 04/21] proper seeding for random determinism in scavenger queuing --- source/Agents/GP/GPTrainingLoop.hpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/source/Agents/GP/GPTrainingLoop.hpp b/source/Agents/GP/GPTrainingLoop.hpp index 8b40a442..cec63651 100644 --- a/source/Agents/GP/GPTrainingLoop.hpp +++ b/source/Agents/GP/GPTrainingLoop.hpp @@ -76,6 +76,7 @@ namespace cowboys { std::vector>> independentAgentFitness = std::vector>>(); int global_max_threads = std::thread::hardware_concurrency(); + int rollingRandomSeed = 0; double ELITE_POPULATION_PERCENT = 0.1; double UNFIT_POPULATION_PERCENT = 0.2; @@ -413,14 +414,14 @@ namespace cowboys { size_t generation = 0; // <- the generation to start at - int tempRandSeed = rand(); + if (std::filesystem::exists(metaDataFullPath) && ScavengerQueuing) { std::cout << "MetaData file exists" << std::endl; metaData.LoadFile(metaDataFullPath.string().c_str()); rootMetaData = metaData.FirstChildElement("GPLoopMetaData"); auto *generationTag = rootMetaData->FirstChildElement(); generation = generationTag->UnsignedAttribute("generation") + 1; - tempRandSeed = generationTag->UnsignedAttribute("randSeed"); + rollingRandomSeed = generationTag->UnsignedAttribute("randSeed"); std::cout << "Starting at generation " << generation << std::endl; } else { @@ -428,13 +429,14 @@ namespace cowboys { { std::cout << "MetaData file does not exist Starting a new Scavenger Queue" << std::endl; } - + rollingRandomSeed = TRAINING_SEED; rootMetaData = metaData.NewElement("GPLoopMetaData"); metaData.InsertFirstChild(rootMetaData); } for (; generation < numGenerations; ++generation) { - srand(tempRandSeed); + srand(rollingRandomSeed); + rollingRandomSeed = rand(); auto generationStartTime = std::chrono::high_resolution_clock::now(); saveDataParams.updateGeneration(generation); @@ -627,7 +629,8 @@ namespace cowboys { generationTag->SetAttribute("averageFitness", averageFitness); generationTag->SetAttribute("maxFitness", maxFitness); generationTag->SetAttribute("bestAgentIDX", bestAgent.second); - generationTag->SetAttribute("Rand", rand()); + + generationTag->SetAttribute("Rand", rollingRandomSeed); rootMetaData->InsertFirstChild(generationTag); From c1e89c362585c17f6e0af664136707aebdfe8bb6 Mon Sep 17 00:00:00 2001 From: JasonMSeguin Date: Tue, 5 Dec 2023 18:28:21 -0500 Subject: [PATCH 05/21] Added deserialization and comments --- source/Agents/GP/LGPAgent.hpp | 141 +++++++++++++++++++++++----------- 1 file changed, 96 insertions(+), 45 deletions(-) diff --git a/source/Agents/GP/LGPAgent.hpp b/source/Agents/GP/LGPAgent.hpp index 39ab9c9a..9baf34bb 100644 --- a/source/Agents/GP/LGPAgent.hpp +++ b/source/Agents/GP/LGPAgent.hpp @@ -1,3 +1,7 @@ +/** + * This file is part of the Fall 2023, CSE 491 course project. + * @brief An Agent based on linear genetic programming. + **/ #pragma once @@ -19,14 +23,14 @@ namespace cowboys { protected: // A dictionary of actions and a dictionary of sensors - // A sensor is a function that takes in a grid and returns a value (e.g. distance to nearest agent) - - // For example group 1 has a function for the shortest path + // A sensor is a function that takes in a grid and returns a value (e.g. distance to left wall) std::vector possibleInstructionsList = {}; std::vector actionsList = {}; std::vector operationsList = {"lessthan", "greaterthan", "equals"}; std::vector sensorsNamesList = {"getLeft", "getRight", "getUp", "getDown"}; + + // A list that stores the results of executed instructions std::vector resultsList; std::vector> instructionsList = {}; @@ -46,9 +50,7 @@ namespace cowboys } } - - - /// @brief This agent needs a specific set of actions to function. + /// @brief Initialize the agent after being given an action map /// @return Success. bool Initialize() override { @@ -57,9 +59,10 @@ namespace cowboys return true; } + /// @brief Generate a random list of instructions from a list of possible instructions + /// @return Success. void GenerateRandomActionList() { - // generate a random list of actions std::uniform_int_distribution dist(0, possibleInstructionsList.size() - 1); std::uniform_int_distribution dist2(0, LISTSIZE - 1); for (int i = 0; i < LISTSIZE; i++) @@ -70,8 +73,12 @@ namespace cowboys /// @brief Encodes the actions from an agent's action map into a vector of string, representing action names. /// @param action_map The action map from the agent. + /// @param sensorsNamesList The list of sensors from the agent. + /// @param operationsList The list of operations from the agent. + /// @param actionsList The list of actions from the agent. /// @return A vector of strings, representing action names. - static std::vector EncodeActions(const std::unordered_map &action_map, const std::vector &sensorsNamesList, const std::vector &operationsList, std::vector &actionsList) + static std::vector EncodeActions(const std::unordered_map &action_map, const std::vector &sensorsNamesList, + const std::vector &operationsList, std::vector &actionsList) { std::vector instructions; for (const auto &[action_name, action_id] : action_map) @@ -92,10 +99,8 @@ namespace cowboys return instructions; } - /** - * @brief Mutate this agent. - * @param mutation_rate - */ + /// @brief Mutate this agent. + /// @param mutation_rate The probability of any instruction being changed void MutateAgent(double mutation_rate = 0.01) override { std::uniform_int_distribution rnd_mutate(1, 100); @@ -110,8 +115,6 @@ namespace cowboys } } - -// resultsList = std::vector(LISTSIZE); resultsList.clear(); resultsList.resize(LISTSIZE); currentInstructionIndex = 0; @@ -141,14 +144,12 @@ namespace cowboys Configure(dynamic_cast(other)); } - /** - * @brief Get the action object - * @param grid - * @param type_options - * @param item_set - * @param agent_set - * @return - */ + /// @brief Get the action to take. + /// @param grid The world grid. + /// @param type_options The available types of cells in the grid. + /// @param item_set The set of items in the world. + /// @param agent_set The set of agents in the world. + /// @return A size_t corresponding to the action chosen size_t GetAction([[maybe_unused]] const cse491::WorldGrid &grid, [[maybe_unused]] const cse491::type_options_t &type_options, [[maybe_unused]] const cse491::item_map_t &item_set, @@ -173,6 +174,7 @@ namespace cowboys { if (std::find(actionsList.begin(), actionsList.end(), std::get<0>(instruction)) != actionsList.end()) { + // the instruction is in the action list (provided by the world) action = std::get<0>(instruction); } else if (std::find(sensorsNamesList.begin(), sensorsNamesList.end(), std::get<0>(instruction)) != sensorsNamesList.end()) @@ -243,11 +245,9 @@ namespace cowboys return 0; } - /** - * @brief Export the agent to a string - * @return - */ - std::string Export() { + /// @brief Export the agent to a string + /// @return The string representation of the agent + std::string Export() override { std::string encodedLists = ""; for (auto instruction : instructionsList) @@ -270,23 +270,19 @@ namespace cowboys encodedLists += ";"; - for (auto auction : actionsList) + for (auto action : actionsList) { - encodedLists += auction; + encodedLists += action; encodedLists += "."; } - encodedLists += ";"; - return encodedLists; } - /** - * @brief Serialize the agent to XML - * @param doc - * @param parentElem - * @param fitness - */ + /// @brief Serialize this agent to XML. + /// @param doc The XML document to serialize to. + /// @param parentElem The parent element to serialize to. + /// @param fitness The fitness of this agent to write to the XML. void Serialize(tinyxml2::XMLDocument & doc, tinyxml2::XMLElement* parentElem, double fitness = -1) override { auto agentElem = doc.NewElement("LGPAgent"); @@ -299,15 +295,70 @@ namespace cowboys agentElem->InsertEndChild(listElem); } - /** - * @brief Print the agent - */ + /// @brief Load in the string representation of an LGP agent and configure this agent based on it. + /// @param genotype The string representation of an LGP agent. + void Import(std::string encodedLists) { + std::vector> decodedInstructionsList = {}; + std::string decodedInstruction; + size_t start_pos = 0; + size_t first_period_pos; + size_t second_period_pos; + size_t comma_pos = encodedLists.find(","); + + // Load the instruction list + while (comma_pos != std::string::npos) { + decodedInstruction = encodedLists.substr(start_pos, comma_pos - start_pos); + first_period_pos = decodedInstruction.find("."); + second_period_pos = decodedInstruction.find(".", first_period_pos + 1); + decodedInstructionsList.push_back(std::make_tuple(decodedInstruction.substr(0, first_period_pos), + std::stoi(decodedInstruction.substr(first_period_pos+1, second_period_pos-first_period_pos+1)), std::stoi(decodedInstruction.substr(second_period_pos+1)))); + + start_pos = comma_pos + 1; + comma_pos = encodedLists.find(",", start_pos); + } + + std::vector decodedPossInstructionsList = {}; + std::vector decodedActionsList = {}; + size_t first_semicolon_pos = encodedLists.find(";"); + size_t second_semicolon_pos = encodedLists.find(";", first_semicolon_pos+1); + std::string unseparated_instruction_list = encodedLists.substr(first_semicolon_pos+1, second_semicolon_pos-first_semicolon_pos+1); + std::string unseparated_action_list = encodedLists.substr(second_semicolon_pos+1); + + // Load the list of possible instructions + size_t period_pos = unseparated_instruction_list.find("."); + start_pos = 0; + while (period_pos != std::string::npos) { + decodedPossInstructionsList.push_back(unseparated_instruction_list.substr(start_pos, period_pos)); + + start_pos = period_pos + 1; + period_pos = encodedLists.find(".", start_pos); + } + + // Load the list of actions + period_pos = unseparated_action_list.find("."); + start_pos = 0; + while (period_pos != std::string::npos) { + decodedActionsList.push_back(unseparated_action_list.substr(start_pos, period_pos)); + + start_pos = period_pos + 1; + period_pos = encodedLists.find(".", start_pos); + } + + instructionsList = decodedInstructionsList; + possibleInstructionsList = decodedPossInstructionsList; + actionsList = decodedActionsList; + resultsList.clear(); + resultsList.resize(LISTSIZE); + currentInstructionIndex = 0; + } + + /// @brief Print the agent void PrintAgent() override { - for (auto i = 0; i < LISTSIZE; i++) - { - std::cout << std::get<0>(instructionsList[i]) << " "; - } - std::cout << std::endl; + for (auto i = 0; i < LISTSIZE; i++) + { + std::cout << std::get<0>(instructionsList[i]) << " "; + } + std::cout << std::endl; } }; } From 683afd3496592b6b013a60fd258b7adc2df129ed Mon Sep 17 00:00:00 2001 From: Aman Dhruva Thamminana <48414198+amantham20@users.noreply.github.com> Date: Tue, 5 Dec 2023 18:29:04 -0500 Subject: [PATCH 06/21] A* fintness function --- source/Agents/GP/GPTrainingLoop.hpp | 63 +++++++++++++++++++++++++++-- source/gp_train_main.cpp | 4 +- 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/source/Agents/GP/GPTrainingLoop.hpp b/source/Agents/GP/GPTrainingLoop.hpp index cec63651..4e1e1e14 100644 --- a/source/Agents/GP/GPTrainingLoop.hpp +++ b/source/Agents/GP/GPTrainingLoop.hpp @@ -296,6 +296,39 @@ namespace cowboys { return fitness; } +// STARTPOSITIONS[startPos_idx], agents[arena][a]->GetPosition(), arena, a + double AStarFitnessFunction(const cse491::GridPosition & startpos, const cse491::GridPosition & endpos, int arena, int a) + { + double fitness = 0; + + // Euclidean distance + cse491::GridPosition currentPosition = endpos; + double distance = static_cast( + Get_A_StarDistance(startpos, currentPosition, arena, a) + ); + + + double score = distance; + + fitness += score; + + cse491::AgentBase &agent = *agents[arena][a]; + // Agent complexity, temporarily doing this in a bad way + if (auto *cgp = dynamic_cast(&agent)){ + auto genotype = cgp->GetGenotype(); + double connection_complexity = + static_cast(genotype.GetNumConnections()) / genotype.GetNumPossibleConnections(); + + double functional_nodes = genotype.GetNumFunctionalNodes(); + double node_complexity = functional_nodes / (functional_nodes + 1); + + double complexity = connection_complexity + node_complexity; + fitness -= complexity; + } + + return fitness; + } + /** * Gets the path of the save location * @return @@ -581,6 +614,15 @@ namespace cowboys { return ss.str(); } + size_t Get_A_StarDistance(const cse491::GridPosition & startpos, const cse491::GridPosition & endpos, int arenaIDX, int agentIDX) { + auto& agent = agents[arenaIDX][agentIDX]; + auto& world = environments[arenaIDX]; + + + auto distance = walle::GetShortestPath(startpos, endpos, *world, *agent).size() - 1; + assert(distance >= 0); + return distance; + } /** @@ -643,6 +685,11 @@ namespace cowboys { Printgrid(endPositions[bestAgent.first][bestAgent.second], 'A'); + auto& agent = agents[bestAgent.first][bestAgent.second]; + auto& startPosition = STARTPOSITIONS; + auto& endPosition = endPositions[bestAgent.first][bestAgent.second]; + + auto& world = environments[bestAgent.first]; auto calculateDistance = [](const cse491::GridPosition& startPosition, const cse491::GridPosition & currentPosition) { @@ -654,14 +701,21 @@ namespace cowboys { std::cout << std::left << std::setw(columnWidth) << "Start" << std::setw(columnWidth) << "Final" - << "Distance\n"; + << std::setw(columnWidth) << "Distance" + << "A* Distance\n"; for (size_t i = 0; i < STARTPOSITIONS.size(); ++i) { std::cout << std::setw(columnWidth) << FormatPosition(STARTPOSITIONS[i]) << std::setw(columnWidth) << FormatPosition(endPositions[bestAgent.first][bestAgent.second][i]); - double distance = calculateDistance(STARTPOSITIONS[i], endPositions[bestAgent.first][bestAgent.second][i]); - std::cout << std::fixed << std::setprecision(2) << std::setw(6) << distance; + // Calculate and print Euclidean distance + double distance = calculateDistance(STARTPOSITIONS[i], endPositions[bestAgent.first][bestAgent.second][i]); + std::cout << std::fixed << std::setprecision(2) << std::setw(12) << distance; + + + // Calculate and print A* distance + size_t astarDistance = Get_A_StarDistance(startPosition[i], endPosition[i], bestAgent.first, bestAgent.second); + std::cout << std::setw(12) << astarDistance; std::cout << std::endl; @@ -981,7 +1035,8 @@ namespace cowboys { environments[arena]->UpdateWorld(); } for (size_t a = 0; a < agents[arena].size(); ++a) { - double tempscore = SimpleFitnessFunction(*agents[arena][a], STARTPOSITIONS[startPos_idx]); +// double tempscore = SimpleFitnessFunction(*agents[arena][a], STARTPOSITIONS[startPos_idx]); + double tempscore = AStarFitnessFunction(STARTPOSITIONS[startPos_idx], agents[arena][a]->GetPosition(), arena, a); auto tempEndPosition = agents[arena][a]->GetPosition(); endPositions[arena][a][startPos_idx] = tempEndPosition; independentAgentFitness[arena][a][startPos_idx] = tempscore; diff --git a/source/gp_train_main.cpp b/source/gp_train_main.cpp index e4897b04..bc7f7384 100644 --- a/source/gp_train_main.cpp +++ b/source/gp_train_main.cpp @@ -21,11 +21,11 @@ int main() { auto start_time = std::chrono::high_resolution_clock::now(); - cowboys::GPTrainingLoop loop(true); + cowboys::GPTrainingLoop loop(false); loop.Initialize(50, 200); - loop.Run(80, 50, num_threads, true); + loop.Run(80, 50, num_threads, false); auto end_time = std::chrono::high_resolution_clock::now(); From 1fde1ef2d2fcdac3d5ee79d0e207727cacda0478 Mon Sep 17 00:00:00 2001 From: Rajmeet Singh Chandok Date: Wed, 6 Dec 2023 04:43:05 -0500 Subject: [PATCH 07/21] Created GPAnalyzer class --- CMakeLists.txt | 3 +- source/Agents/GP/GPAgentAnalyze.h | 64 +++++++++++++++++++++++++++++ source/Agents/GP/GPTrainingLoop.hpp | 17 ++++++-- source/gp_train_main.cpp | 6 +-- 4 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 source/Agents/GP/GPAgentAnalyze.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bba2f19..a6b8615c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,7 +131,8 @@ if (${BUILD_MAIN}) string(REPLACE ".cpp" "" EXE_NAME ${EXE_SOURCE}) # Create list of source files (currently just the one .cpp file) # Create executable and link to includes / libraries - add_executable(${EXE_NAME} ${CMAKE_SOURCE_DIR}/source/${EXE_SOURCE} ${CMAKE_SOURCE_DIR}/source/core/Entity.cpp) + add_executable(${EXE_NAME} ${CMAKE_SOURCE_DIR}/source/${EXE_SOURCE} ${CMAKE_SOURCE_DIR}/source/core/Entity.cpp + source/Agents/GP/GPAgentAnalyze.h) target_include_directories(${EXE_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/source ) diff --git a/source/Agents/GP/GPAgentAnalyze.h b/source/Agents/GP/GPAgentAnalyze.h new file mode 100644 index 00000000..db31793e --- /dev/null +++ b/source/Agents/GP/GPAgentAnalyze.h @@ -0,0 +1,64 @@ +// +// Created by rajme on 12/4/2023. +// + +#pragma once + +#include +#include +#include +#include + +namespace cowboys { + + class GPAgentAnalyzer { + private: + std::vector average_fitness; + std::vector max_fitness; + std::vector elite_score; + std::vector average_score; + std::vector max_agents; + + public: + GPAgentAnalyzer() = default; + ~GPAgentAnalyzer() = default; + + void addAverageFitness(double fitness) { + average_fitness.push_back(fitness); + } + + void addMaxFitness(double fitness) { + max_fitness.push_back(fitness); + } + + void addEliteScore(double score) { + elite_score.push_back(score); + } + + void addAverageScore(double score) { + average_score.push_back(score); + } + void addNumAgentsWithMaxFitness(double num_agents) { + max_agents.push_back(num_agents); + } + + void saveToFile() { + // create a new file + std::ofstream file("gp_agent_analyzer.csv"); + + // write the data to the file + file << "average_fitness,max_fitness,average_elite_score,best_agent_weighted_score,agents_with_max_fitness\n"; + + for (int i = 0; i < average_fitness.size(); i++) { + file << average_fitness[i] << "," << max_fitness[i] << "," << elite_score[i] << "," << average_score[i] << "," << max_agents[i] << "\n"; + } + + // close the file + file.close(); + + + } + }; + +} // namespace cowboys + diff --git a/source/Agents/GP/GPTrainingLoop.hpp b/source/Agents/GP/GPTrainingLoop.hpp index cec63651..b9aee79c 100644 --- a/source/Agents/GP/GPTrainingLoop.hpp +++ b/source/Agents/GP/GPTrainingLoop.hpp @@ -21,7 +21,7 @@ #include "tinyxml2.h" - +#include "GPAgentAnalyze.h" namespace cowboys { @@ -36,6 +36,7 @@ namespace cowboys { std::vector> agents; std::vector> TEMPAgentFitness; + GPAgentAnalyzer analyzer; tinyxml2::XMLDocument topAgentsDoc; tinyxml2::XMLDocument lastGenerationsTopAgentsDoc; // <- Saves the last 5 generations @@ -454,6 +455,7 @@ namespace cowboys { saveDataParams.countMaxAgents = countMaxAgents; SaveDataCheckPoint(saveDataParams); + GpLoopMutateHelper(); resetEnvironments(); @@ -462,6 +464,7 @@ namespace cowboys { generationEndTime - generationStartTime); std::cout << "Generation " << generation << " took " << generationDuration.count() / 1000000.0 << " seconds" << std::endl; + analyzer.saveToFile(); } @@ -564,6 +567,7 @@ namespace cowboys { std::cout << "@@@@@@@@@@@@@@@@@@@@@@ " << "DataSaved" << " @@@@@@@@@@@@@@@@@@@@@@" << std::endl; +// analyzer.saveToFile(getSystemPath() / "fitness.csv"); lastGenerationsTopAgentsDoc.Clear(); ResetMainTagLastGenerations(); } @@ -621,7 +625,11 @@ namespace cowboys { std::cout << "Generation " << generation << " complete" << std::endl; std::cout << "Average fitness: " << averageFitness << " "; + analyzer.addAverageFitness(averageFitness); + std::cout << "Max fitness: " << maxFitness << std::endl; + analyzer.addMaxFitness(maxFitness); + std::string tagName = "generation_" + std::to_string(generation); auto *generationTag = metaData.NewElement(tagName.c_str()); @@ -667,12 +675,13 @@ namespace cowboys { std::cout << std::endl; } - std::cout << "with an average score of " << TEMPAgentFitness[bestAgent.first][bestAgent.second] << std::endl; + std::cout << "with best agent weighted score of " << TEMPAgentFitness[bestAgent.first][bestAgent.second] << std::endl; std::cout << std::endl; - + analyzer.addAverageScore(TEMPAgentFitness[bestAgent.first][bestAgent.second]); std::cout << "Number of agents with max fitness: " << countMaxAgents << std::endl; std::cout << "------------------------------------------------------------------" << std::endl; + analyzer.addNumAgentsWithMaxFitness(countMaxAgents); return countMaxAgents; } @@ -809,7 +818,7 @@ namespace cowboys { averageEliteFitness /= ELITE_POPULATION_SIZE; std::cout << " --- average elite score " << averageEliteFitness << "------ " << std::endl; - + analyzer.addEliteScore(averageEliteFitness); const int MIDDLE_MUTATE_ENDBOUND = int(sortedAgents.size() * (1 - UNFIT_POPULATION_PERCENT)); const int MIDDLE_MUTATE_STARTBOUND = int(ELITE_POPULATION_PERCENT * sortedAgents.size()); diff --git a/source/gp_train_main.cpp b/source/gp_train_main.cpp index e4897b04..853f69ab 100644 --- a/source/gp_train_main.cpp +++ b/source/gp_train_main.cpp @@ -21,11 +21,11 @@ int main() { auto start_time = std::chrono::high_resolution_clock::now(); - cowboys::GPTrainingLoop loop(true); + cowboys::GPTrainingLoop loop(false); - loop.Initialize(50, 200); - loop.Run(80, 50, num_threads, true); + loop.Initialize(10, 100); + loop.Run(80, 50, num_threads, false); auto end_time = std::chrono::high_resolution_clock::now(); From 6e1b928dc037f3b9c6bfd32b5f95dccfc01a45b7 Mon Sep 17 00:00:00 2001 From: Aman Dhruva Thamminana <48414198+amantham20@users.noreply.github.com> Date: Tue, 5 Dec 2023 18:29:04 -0500 Subject: [PATCH 08/21] A* fintness function --- source/Agents/GP/GPTrainingLoop.hpp | 63 +++++++++++++++++++++++++++-- source/gp_train_main.cpp | 2 +- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/source/Agents/GP/GPTrainingLoop.hpp b/source/Agents/GP/GPTrainingLoop.hpp index b9aee79c..12f9b8d0 100644 --- a/source/Agents/GP/GPTrainingLoop.hpp +++ b/source/Agents/GP/GPTrainingLoop.hpp @@ -297,6 +297,39 @@ namespace cowboys { return fitness; } +// STARTPOSITIONS[startPos_idx], agents[arena][a]->GetPosition(), arena, a + double AStarFitnessFunction(const cse491::GridPosition & startpos, const cse491::GridPosition & endpos, int arena, int a) + { + double fitness = 0; + + // Euclidean distance + cse491::GridPosition currentPosition = endpos; + double distance = static_cast( + Get_A_StarDistance(startpos, currentPosition, arena, a) + ); + + + double score = distance; + + fitness += score; + + cse491::AgentBase &agent = *agents[arena][a]; + // Agent complexity, temporarily doing this in a bad way + if (auto *cgp = dynamic_cast(&agent)){ + auto genotype = cgp->GetGenotype(); + double connection_complexity = + static_cast(genotype.GetNumConnections()) / genotype.GetNumPossibleConnections(); + + double functional_nodes = genotype.GetNumFunctionalNodes(); + double node_complexity = functional_nodes / (functional_nodes + 1); + + double complexity = connection_complexity + node_complexity; + fitness -= complexity; + } + + return fitness; + } + /** * Gets the path of the save location * @return @@ -585,6 +618,15 @@ namespace cowboys { return ss.str(); } + size_t Get_A_StarDistance(const cse491::GridPosition & startpos, const cse491::GridPosition & endpos, int arenaIDX, int agentIDX) { + auto& agent = agents[arenaIDX][agentIDX]; + auto& world = environments[arenaIDX]; + + + auto distance = walle::GetShortestPath(startpos, endpos, *world, *agent).size() - 1; + assert(distance >= 0); + return distance; + } /** @@ -651,6 +693,11 @@ namespace cowboys { Printgrid(endPositions[bestAgent.first][bestAgent.second], 'A'); + auto& agent = agents[bestAgent.first][bestAgent.second]; + auto& startPosition = STARTPOSITIONS; + auto& endPosition = endPositions[bestAgent.first][bestAgent.second]; + + auto& world = environments[bestAgent.first]; auto calculateDistance = [](const cse491::GridPosition& startPosition, const cse491::GridPosition & currentPosition) { @@ -662,14 +709,21 @@ namespace cowboys { std::cout << std::left << std::setw(columnWidth) << "Start" << std::setw(columnWidth) << "Final" - << "Distance\n"; + << std::setw(columnWidth) << "Distance" + << "A* Distance\n"; for (size_t i = 0; i < STARTPOSITIONS.size(); ++i) { std::cout << std::setw(columnWidth) << FormatPosition(STARTPOSITIONS[i]) << std::setw(columnWidth) << FormatPosition(endPositions[bestAgent.first][bestAgent.second][i]); - double distance = calculateDistance(STARTPOSITIONS[i], endPositions[bestAgent.first][bestAgent.second][i]); - std::cout << std::fixed << std::setprecision(2) << std::setw(6) << distance; + // Calculate and print Euclidean distance + double distance = calculateDistance(STARTPOSITIONS[i], endPositions[bestAgent.first][bestAgent.second][i]); + std::cout << std::fixed << std::setprecision(2) << std::setw(12) << distance; + + + // Calculate and print A* distance + size_t astarDistance = Get_A_StarDistance(startPosition[i], endPosition[i], bestAgent.first, bestAgent.second); + std::cout << std::setw(12) << astarDistance; std::cout << std::endl; @@ -990,7 +1044,8 @@ namespace cowboys { environments[arena]->UpdateWorld(); } for (size_t a = 0; a < agents[arena].size(); ++a) { - double tempscore = SimpleFitnessFunction(*agents[arena][a], STARTPOSITIONS[startPos_idx]); +// double tempscore = SimpleFitnessFunction(*agents[arena][a], STARTPOSITIONS[startPos_idx]); + double tempscore = AStarFitnessFunction(STARTPOSITIONS[startPos_idx], agents[arena][a]->GetPosition(), arena, a); auto tempEndPosition = agents[arena][a]->GetPosition(); endPositions[arena][a][startPos_idx] = tempEndPosition; independentAgentFitness[arena][a][startPos_idx] = tempscore; diff --git a/source/gp_train_main.cpp b/source/gp_train_main.cpp index 853f69ab..bc7f7384 100644 --- a/source/gp_train_main.cpp +++ b/source/gp_train_main.cpp @@ -24,7 +24,7 @@ int main() { cowboys::GPTrainingLoop loop(false); - loop.Initialize(10, 100); + loop.Initialize(50, 200); loop.Run(80, 50, num_threads, false); From a6de9dbce9750243c9c4d95eb0350614ef92c323 Mon Sep 17 00:00:00 2001 From: Aman Dhruva Thamminana <48414198+amantham20@users.noreply.github.com> Date: Tue, 5 Dec 2023 18:39:42 -0500 Subject: [PATCH 09/21] removed the duplicate export From c1e1a6800e1ed9cc8f80655f704ac208e16404a1 Mon Sep 17 00:00:00 2001 From: Aman Dhruva Thamminana <48414198+amantham20@users.noreply.github.com> Date: Wed, 6 Dec 2023 04:45:14 -0500 Subject: [PATCH 10/21] runner class for gp --- CMakeLists.txt | 15 ++++ source/gp_selective_runner_main.cpp | 108 ++++++++++++++++++++++++++++ source/gp_train_main.cpp | 8 +-- 3 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 source/gp_selective_runner_main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bba2f19..823a4a60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,12 +86,27 @@ if (BUILD_GP_ONLY) #added the executable add_executable(gp_train_main source/gp_train_main.cpp) + add_executable(gp_selective_runner_main source/gp_selective_runner_main.cpp) + + target_sources(gp_selective_runner_main PRIVATE source/core/Entity.cpp) + target_include_directories(gp_selective_runner_main + PRIVATE ${CMAKE_SOURCE_DIR}/source/core + ${CMAKE_SOURCE_DIR}/source/Agents + ) + + target_link_libraries(gp_selective_runner_main + PRIVATE tinyxml2 + PRIVATE pthread + ) + #linking the targets target_sources(gp_train_main PRIVATE source/core/Entity.cpp) target_include_directories(gp_train_main PRIVATE ${CMAKE_SOURCE_DIR}/source/core ${CMAKE_SOURCE_DIR}/source/Agents ) + + target_link_libraries(gp_train_main PRIVATE tinyxml2 PRIVATE pthread diff --git a/source/gp_selective_runner_main.cpp b/source/gp_selective_runner_main.cpp new file mode 100644 index 00000000..9eb7a1b3 --- /dev/null +++ b/source/gp_selective_runner_main.cpp @@ -0,0 +1,108 @@ +/** + * This file is part of the Fall 2023, CSE 491 course project. + * @brief A simplistic main file to demonstrate a system. + * @note Status: PROPOSAL + **/ + +// Include the modules that we will be using. +#include "Agents/PacingAgent.hpp" +#include "Interfaces/TrashInterface.hpp" +#include "Worlds/MazeWorld.hpp" + +#include "Agents/GP/GPAgentBase.hpp" +#include "Agents/GP/LGPAgent.hpp" +#include "Agents/GP/CGPAgent.hpp" + +#include + + +void Printgrid(const cse491::MazeWorld &mazeWorld, char symbol = 'S', std::vector agents = {}) { + + + size_t arena = 0; + auto &grid = mazeWorld.GetGrid(arena); + std::vector symbol_grid(grid.GetHeight()); + + + + + + const auto &type_options = mazeWorld.GetCellTypes(); + // Load the world into the symbol_grid; + for (size_t y = 0; y < grid.GetHeight(); ++y) { + symbol_grid[y].resize(grid.GetWidth()); + for (size_t x = 0; x < grid.GetWidth(); ++x) { + symbol_grid[y][x] = type_options[grid.At(x, y)].symbol; + } + } + + + // Load the agents into the symbol_grid; + for (auto agent : agents) { + auto position = agent->GetPosition(); + symbol_grid[position.GetY()][position.GetX()] = symbol; + } + + std::cout << " "; + for (size_t x = 0; x < grid.GetWidth(); ++x) { + if (x % 10 == 0 && x != 0) { + std::cout << x / 10; // Print the ten's place of the column number + } else { + std::cout << " "; // Space for non-marker columns + } + } + std::cout << "\n"; + + // Print column numbers + std::cout << " "; // Space for row numbers + for (size_t x = 0; x < grid.GetWidth(); ++x) { + std::cout << x % 10; // Print only the last digit of the column number + } + std::cout << "\n"; + + // Print out the symbol_grid with a box around it. + std::cout << " +" << std::string(grid.GetWidth(), '-') << "+\n"; + for (size_t y = 0; y < grid.GetHeight(); ++y) { + + if (y % 10 == 0 && y != 0) { + std::cout << y / 10 << " "; // Print the ten's place of the row number + } else { + std::cout << " "; // Space for non-marker rows + } + + // Print row number + std::cout << y % 10 << "|"; // Print only the last digit of the row number + for (char cell: symbol_grid[y]) { + std::cout << cell; + } + std::cout << "|\n"; + } + + std::cout << " +" << std::string(grid.GetWidth(), '-') << "+\n"; + std::cout << std::endl; +} + + +int main() { + cse491::MazeWorld world; +/** + + 6,4,3,9,6;0.j.606uA3:0.0.61duDu:0.3.600:0.0.60iLO:0.5.602j4n:0.n.603vSU:0.6.614Prx:0.g.61xFL:0.l.602iIM:0.h.60185j:00w.3.619bwS:40g.n.619V/2:0.3.611Z3a:2w0.k.601p/w:40g.l.61iZK:0.m.602U29:0.0.601RtI:1g0.8.603nKR:0.4.604iMQ:0.m.61g6tL:0.j.602df1:a8j8.3.61bYRZ:0.f.612vfs:a084.3.6114Xe:0A60.e.614pTw:0.c.613CKA:0.j.611NQG:0.3.60765e:000e1w.f.602oxB:00085g.l.601xVk:000i1c.m.600: + + */ + + //GP agent + auto& GPagent = static_cast(world.AddAgent("GP 1").SetProperty("symbol", 'G')); + GPagent.Import("6,4,3,9,6;0.j.606uA3:0.0.61duDu:0.3.600:0.0.60iLO:0.5.602j4n:0.n.603vSU:0.6.614Prx:0.g.61xFL:0.l.602iIM:0.h.60185j:00w.3.619bwS:40g.n.619V/2:0.3.611Z3a:2w0.k.601p/w:40g.l.61iZK:0.m.602U29:0.0.601RtI:1g0.8.603nKR:0.4.604iMQ:0.m.61g6tL:0.j.602df1:a8j8.3.61bYRZ:0.f.612vfs:a084.3.6114Xe:0A60.e.614pTw:0.c.613CKA:0.j.611NQG:0.3.60765e:000e1w.f.602oxB:00085g.l.601xVk:000i1c.m.600:"); + GPagent.SetPosition(0, 1); + for (int i = 0; i < 100; ++i) { + world.RunAgents(); + + world.UpdateWorld(); + std::cout << "Generation: " << i << std::endl; + Printgrid(world, 'G', {&GPagent}); + + usleep(170000); + } + +} diff --git a/source/gp_train_main.cpp b/source/gp_train_main.cpp index bc7f7384..9b43ea97 100644 --- a/source/gp_train_main.cpp +++ b/source/gp_train_main.cpp @@ -15,7 +15,7 @@ int main() { const int num_threads = std::thread::hardware_concurrency(); -// const int num_threads = 12; +// const int num_threads = 6; std::cout << "Number of threads: " << num_threads << std::endl; @@ -24,8 +24,8 @@ int main() { cowboys::GPTrainingLoop loop(false); - loop.Initialize(50, 200); - loop.Run(80, 50, num_threads, false); + loop.Initialize(13, 200); + loop.Run(6, 50, num_threads, true); auto end_time = std::chrono::high_resolution_clock::now(); @@ -34,7 +34,5 @@ int main() { auto seconds = duration.count() / 1000000.0; std::cout << "Time taken by Training: " << seconds << " seconds" << std::endl; - - return 0; } \ No newline at end of file From 5a2d05bc5874b1a32346645590dfe3fc1ab4dfb5 Mon Sep 17 00:00:00 2001 From: Aman Dhruva Thamminana <48414198+amantham20@users.noreply.github.com> Date: Wed, 6 Dec 2023 12:21:36 -0500 Subject: [PATCH 11/21] updated things --- CMakeLists.txt | 40 ++++++++++------------------- source/gp_selective_runner_main.cpp | 3 ++- source/gp_train_cgp_main.cpp | 38 +++++++++++++++++++++++++++ source/gp_train_lgp_main.cpp | 38 +++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 27 deletions(-) create mode 100644 source/gp_train_cgp_main.cpp create mode 100644 source/gp_train_lgp_main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 823a4a60..34280074 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,39 +78,27 @@ if (BUILD_GP_ONLY) set(BUILD_MAIN 0) set(BUILD_TESTS 0) - # Configure all of tinyxml2 set(XML_SRC_DIR third_party/tinyxml2) set(XML_BUILD_DIR xml_build) add_subdirectory(${XML_SRC_DIR} ${XML_BUILD_DIR}) - #added the executable - add_executable(gp_train_main source/gp_train_main.cpp) + # List of executables + set(EXECUTABLES gp_train_main gp_selective_runner_main gp_train_cgp_main gp_train_lgp_main) - add_executable(gp_selective_runner_main source/gp_selective_runner_main.cpp) + # Common source files and include directories + set(COMMON_SOURCES source/core/Entity.cpp) + set(COMMON_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/source/core ${CMAKE_SOURCE_DIR}/source/Agents) - target_sources(gp_selective_runner_main PRIVATE source/core/Entity.cpp) - target_include_directories(gp_selective_runner_main - PRIVATE ${CMAKE_SOURCE_DIR}/source/core - ${CMAKE_SOURCE_DIR}/source/Agents - ) + # Common libraries to link with + set(COMMON_LIBRARIES tinyxml2 pthread) - target_link_libraries(gp_selective_runner_main - PRIVATE tinyxml2 - PRIVATE pthread - ) - - #linking the targets - target_sources(gp_train_main PRIVATE source/core/Entity.cpp) - target_include_directories(gp_train_main - PRIVATE ${CMAKE_SOURCE_DIR}/source/core - ${CMAKE_SOURCE_DIR}/source/Agents - ) - - - target_link_libraries(gp_train_main - PRIVATE tinyxml2 - PRIVATE pthread - ) + # Loop to configure each executable + foreach(EXECUTABLE IN LISTS EXECUTABLES) + add_executable(${EXECUTABLE} source/${EXECUTABLE}.cpp) + target_sources(${EXECUTABLE} PRIVATE ${COMMON_SOURCES}) + target_include_directories(${EXECUTABLE} PRIVATE ${COMMON_INCLUDE_DIRS}) + target_link_libraries(${EXECUTABLE} PRIVATE ${COMMON_LIBRARIES}) + endforeach() endif () diff --git a/source/gp_selective_runner_main.cpp b/source/gp_selective_runner_main.cpp index 9eb7a1b3..5a9b7762 100644 --- a/source/gp_selective_runner_main.cpp +++ b/source/gp_selective_runner_main.cpp @@ -102,7 +102,8 @@ int main() { std::cout << "Generation: " << i << std::endl; Printgrid(world, 'G', {&GPagent}); - usleep(170000); + constexpr const int kMicrosecondsPerSecond = 170000; + usleep(kMicrosecondsPerSecond); } } diff --git a/source/gp_train_cgp_main.cpp b/source/gp_train_cgp_main.cpp new file mode 100644 index 00000000..57c29219 --- /dev/null +++ b/source/gp_train_cgp_main.cpp @@ -0,0 +1,38 @@ + +#include "Agents/GP/GPTrainingLoop.hpp" +#include "Agents/GP/LGPAgent.hpp" +#include "Worlds/MazeWorld.hpp" + +#include "Interfaces/TrashInterface.hpp" +#include "Worlds/ManualWorld.hpp" + + +#include + + +#include + +int main() { + +// const int num_threads = std::thread::hardware_concurrency(); + const int num_threads = 6; + std::cout << "Number of threads: " << num_threads << std::endl; + + + auto start_time = std::chrono::high_resolution_clock::now(); + + cowboys::GPTrainingLoop loop(true); + + + loop.Initialize(50, 5000); + loop.Run(200, 70, num_threads, true); + + + auto end_time = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end_time - start_time); + + auto seconds = duration.count() / 1000000.0; + std::cout << "Time taken by Training: " << seconds << " seconds" << std::endl; + + return 0; +} \ No newline at end of file diff --git a/source/gp_train_lgp_main.cpp b/source/gp_train_lgp_main.cpp new file mode 100644 index 00000000..00426f99 --- /dev/null +++ b/source/gp_train_lgp_main.cpp @@ -0,0 +1,38 @@ + +#include "Agents/GP/GPTrainingLoop.hpp" +#include "Agents/GP/LGPAgent.hpp" +#include "Worlds/MazeWorld.hpp" + +#include "Interfaces/TrashInterface.hpp" +#include "Worlds/ManualWorld.hpp" + + +#include + + +#include + +int main() { + +// const int num_threads = std::thread::hardware_concurrency(); + const int num_threads = 6; + std::cout << "Number of threads: " << num_threads << std::endl; + + + auto start_time = std::chrono::high_resolution_clock::now(); + + cowboys::GPTrainingLoop loop(false); + + + loop.Initialize(50, 5000); + loop.Run(150, 70, num_threads, false); + + + auto end_time = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end_time - start_time); + + auto seconds = duration.count() / 1000000.0; + std::cout << "Time taken by Training: " << seconds << " seconds" << std::endl; + + return 0; +} \ No newline at end of file From 9fab640fbfd60d5e2a03303327186963f7c7d766 Mon Sep 17 00:00:00 2001 From: Aman Dhruva Thamminana <48414198+amantham20@users.noreply.github.com> Date: Mon, 27 Nov 2023 20:55:04 -0500 Subject: [PATCH 12/21] =?UTF-8?q?Abracadabra!=20Warnings=20Vanish!=20?= =?UTF-8?q?=F0=9F=8E=A9=F0=9F=90=87=20Pulled=20a=20rabbit=20out=20of=20my?= =?UTF-8?q?=20hat=20and=20made=20all=20those=20warnings=20disappear.=20Now?= =?UTF-8?q?,=20that's=20what=20I=20call=20a=20magic=20trick!=20#CodeMagici?= =?UTF-8?q?an?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/Agents/GP/GPAgentBase.hpp | 1 + source/Agents/GP/GPTrainingLoop.hpp | 10 ++++++---- source/Agents/GP/LGPAgent.hpp | 2 +- source/core/Data.hpp | 7 ++++--- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/source/Agents/GP/GPAgentBase.hpp b/source/Agents/GP/GPAgentBase.hpp index 962d48f6..28128d86 100644 --- a/source/Agents/GP/GPAgentBase.hpp +++ b/source/Agents/GP/GPAgentBase.hpp @@ -65,6 +65,7 @@ namespace cowboys { virtual void Serialize(tinyxml2::XMLDocument &doc, tinyxml2::XMLElement *parentElem, double fitness = -1) = 0; + /// TODO: Might have to remove it virtual std::string Export() { return ""; } virtual void Reset(bool /*hard*/ = false) { extra_state["previous_action"] = 0; }; diff --git a/source/Agents/GP/GPTrainingLoop.hpp b/source/Agents/GP/GPTrainingLoop.hpp index ada100d0..f303805b 100644 --- a/source/Agents/GP/GPTrainingLoop.hpp +++ b/source/Agents/GP/GPTrainingLoop.hpp @@ -482,7 +482,8 @@ namespace cowboys { std::cout << "Average fitness: " << averageFitness << " "; std::cout << "Max fitness: " << maxFitness << std::endl; - const char *tagName = ("generation_" + std::to_string(generation)).c_str(); + std::string tempName = "generation_" + std::to_string(generation); + const char *tagName = tempName.c_str(); auto *generationTag = metaData.NewElement(tagName); @@ -573,7 +574,8 @@ namespace cowboys { void SerializeAgents(int generation, tinyxml2::XMLElement *rootElement, tinyxml2::XMLDocument ¶mDocument, size_t topN = 5) { - const char *tagName = ("generation_" + std::to_string(generation)).c_str(); + std::string tempName = "generation_" + std::to_string(generation); + const char *tagName = tempName.c_str(); auto *generationTag = paramDocument.NewElement(tagName); @@ -851,7 +853,7 @@ namespace cowboys { for (size_t a = 0; a < agents[arena].size(); ++a) { std::vector scores = independentAgentFitness[arena][a]; - auto computeMedian = [&scores]() -> double { + [[maybe_unused]] auto computeMedian = [&scores]() -> double { std::vector temp(scores); // Copy the data std::sort(temp.begin(), temp.end()); @@ -863,7 +865,7 @@ namespace cowboys { // TEMPAgentFitness[arena][a] /= STARTPOSITIONS.size(); // TEMPAgentFitness[arena][a] += computeMedian(); double min = *std::min_element(scores.begin(), scores.end()); - double avg = TEMPAgentFitness[arena][a] / STARTPOSITIONS.size(); + [[maybe_unused]] double avg = TEMPAgentFitness[arena][a] / STARTPOSITIONS.size(); // TEMPAgentFitness[arena][a] = 0.7 * min + 0.3 * avg; TEMPAgentFitness[arena][a] = min; } diff --git a/source/Agents/GP/LGPAgent.hpp b/source/Agents/GP/LGPAgent.hpp index 427927ce..c63a6ffc 100644 --- a/source/Agents/GP/LGPAgent.hpp +++ b/source/Agents/GP/LGPAgent.hpp @@ -240,7 +240,7 @@ namespace cowboys return 0; } - void Serialize(tinyxml2::XMLDocument &, tinyxml2::XMLElement *, double fitness = -1) override {} + void Serialize(tinyxml2::XMLDocument &, tinyxml2::XMLElement *, [[maybe_unused]] double fitness = -1) override {} void PrintAgent() override { for (auto i = 0; i < LISTSIZE; i++) diff --git a/source/core/Data.hpp b/source/core/Data.hpp index 81532f80..6c9f6dbd 100644 --- a/source/core/Data.hpp +++ b/source/core/Data.hpp @@ -37,9 +37,10 @@ namespace cse491 { bool HasProperty(const std::string& property) const { return properties.count(property); } - - constexpr static char CELL_WALL[] = "wall"; - constexpr static char CELL_WATER[] = "water"; + + constexpr static const char* CELL_WALL = "wall"; + constexpr static const char* CELL_WATER = "water"; + }; /// @brief Available CellTypes will be passed around as a vector of options. From 9ceff978d1960377060417002bd4db780fd2ab81 Mon Sep 17 00:00:00 2001 From: Aman Dhruva Thamminana <48414198+amantham20@users.noreply.github.com> Date: Tue, 5 Dec 2023 18:40:24 -0500 Subject: [PATCH 13/21] Merge branch 'Jason's-workspace' into GPLoop-amanXSimon-CGPA --- source/Agents/GP/LGPAgent.hpp | 163 ++++++++++++++++++++++++++++------ 1 file changed, 138 insertions(+), 25 deletions(-) diff --git a/source/Agents/GP/LGPAgent.hpp b/source/Agents/GP/LGPAgent.hpp index c63a6ffc..5905ffeb 100644 --- a/source/Agents/GP/LGPAgent.hpp +++ b/source/Agents/GP/LGPAgent.hpp @@ -1,3 +1,7 @@ +/** + * This file is part of the Fall 2023, CSE 491 course project. + * @brief An Agent based on linear genetic programming. + **/ #pragma once @@ -19,14 +23,14 @@ namespace cowboys { protected: // A dictionary of actions and a dictionary of sensors - // A sensor is a function that takes in a grid and returns a value (e.g. distance to nearest agent) - - // For example group 1 has a function for the shortest path + // A sensor is a function that takes in a grid and returns a value (e.g. distance to left wall) std::vector possibleInstructionsList = {}; std::vector actionsList = {}; std::vector operationsList = {"lessthan", "greaterthan", "equals"}; std::vector sensorsNamesList = {"getLeft", "getRight", "getUp", "getDown"}; + + // A list that stores the results of executed instructions std::vector resultsList; std::vector> instructionsList = {}; @@ -46,9 +50,7 @@ namespace cowboys } } - - - /// @brief This agent needs a specific set of actions to function. + /// @brief Initialize the agent after being given an action map /// @return Success. bool Initialize() override { @@ -57,22 +59,26 @@ namespace cowboys return true; } + /// @brief Generate a random list of instructions from a list of possible instructions + /// @return Success. void GenerateRandomActionList() { - // generate a random list of actions std::uniform_int_distribution dist(0, possibleInstructionsList.size() - 1); std::uniform_int_distribution dist2(0, LISTSIZE - 1); for (int i = 0; i < LISTSIZE; i++) { instructionsList.push_back(std::make_tuple(possibleInstructionsList[dist(gen)], dist2(gen), dist2(gen))); } - } /// @brief Encodes the actions from an agent's action map into a vector of string, representing action names. /// @param action_map The action map from the agent. + /// @param sensorsNamesList The list of sensors from the agent. + /// @param operationsList The list of operations from the agent. + /// @param actionsList The list of actions from the agent. /// @return A vector of strings, representing action names. - static std::vector EncodeActions(const std::unordered_map &action_map, const std::vector &sensorsNamesList, const std::vector &operationsList, std::vector &actionsList) + static std::vector EncodeActions(const std::unordered_map &action_map, const std::vector &sensorsNamesList, + const std::vector &operationsList, std::vector &actionsList) { std::vector instructions; for (const auto &[action_name, action_id] : action_map) @@ -93,7 +99,8 @@ namespace cowboys return instructions; } - + /// @brief Mutate this agent. + /// @param mutation_rate The probability of any instruction being changed void MutateAgent(double mutation_rate = 0.01) override { std::uniform_int_distribution rnd_mutate(1, 100); @@ -108,8 +115,6 @@ namespace cowboys } } - -// resultsList = std::vector(LISTSIZE); resultsList.clear(); resultsList.resize(LISTSIZE); currentInstructionIndex = 0; @@ -139,10 +144,13 @@ namespace cowboys Configure(dynamic_cast(other)); } - std::string Export() override { - return ""; - } + /// @brief Get the action to take. + /// @param grid The world grid. + /// @param type_options The available types of cells in the grid. + /// @param item_set The set of items in the world. + /// @param agent_set The set of agents in the world. + /// @return A size_t corresponding to the action chosen size_t GetAction([[maybe_unused]] const cse491::WorldGrid &grid, [[maybe_unused]] const cse491::type_options_t &type_options, [[maybe_unused]] const cse491::item_map_t &item_set, @@ -167,6 +175,7 @@ namespace cowboys { if (std::find(actionsList.begin(), actionsList.end(), std::get<0>(instruction)) != actionsList.end()) { + // the instruction is in the action list (provided by the world) action = std::get<0>(instruction); } else if (std::find(sensorsNamesList.begin(), sensorsNamesList.end(), std::get<0>(instruction)) != sensorsNamesList.end()) @@ -176,11 +185,8 @@ namespace cowboys SensorDirection direction = Sensors::getSensorDirectionEnum(sensor); int distance = Sensors::wallDistance(grid, *this, direction); - - resultsList[currentInstructionIndex] = distance; - } else { @@ -240,14 +246,121 @@ namespace cowboys return 0; } - void Serialize(tinyxml2::XMLDocument &, tinyxml2::XMLElement *, [[maybe_unused]] double fitness = -1) override {} + /// @brief Export the agent to a string + /// @return The string representation of the agent + std::string Export() override { + std::string encodedLists = ""; + + for (auto instruction : instructionsList) + { + encodedLists += std::get<0>(instruction); + encodedLists += "."; + encodedLists += std::to_string(std::get<1>(instruction)); + encodedLists += "."; + encodedLists += std::to_string(std::get<2>(instruction)); + encodedLists += ","; + } + + encodedLists += ";"; + + for (auto possInstruction : possibleInstructionsList) + { + encodedLists += possInstruction; + encodedLists += "."; + } + + encodedLists += ";"; + + for (auto action : actionsList) + { + encodedLists += action; + encodedLists += "."; + } + + return encodedLists; + } + + /// @brief Serialize this agent to XML. + /// @param doc The XML document to serialize to. + /// @param parentElem The parent element to serialize to. + /// @param fitness The fitness of this agent to write to the XML. + void Serialize(tinyxml2::XMLDocument & doc, tinyxml2::XMLElement* parentElem, double fitness = -1) override + { + auto agentElem = doc.NewElement("LGPAgent"); + parentElem->InsertEndChild(agentElem); + + auto listElem = doc.NewElement("instruction list"); + listElem->SetText(Export().c_str()); + if (fitness != -1) + listElem->SetAttribute("fitness", fitness); + agentElem->InsertEndChild(listElem); + } + + /// @brief Load in the string representation of an LGP agent and configure this agent based on it. + /// @param genotype The string representation of an LGP agent. + void Import(std::string encodedLists) { + std::vector> decodedInstructionsList = {}; + std::string decodedInstruction; + size_t start_pos = 0; + size_t first_period_pos; + size_t second_period_pos; + size_t comma_pos = encodedLists.find(","); + + // Load the instruction list + while (comma_pos != std::string::npos) { + decodedInstruction = encodedLists.substr(start_pos, comma_pos - start_pos); + first_period_pos = decodedInstruction.find("."); + second_period_pos = decodedInstruction.find(".", first_period_pos + 1); + decodedInstructionsList.push_back(std::make_tuple(decodedInstruction.substr(0, first_period_pos), + std::stoi(decodedInstruction.substr(first_period_pos+1, second_period_pos-first_period_pos+1)), std::stoi(decodedInstruction.substr(second_period_pos+1)))); + + start_pos = comma_pos + 1; + comma_pos = encodedLists.find(",", start_pos); + } + + std::vector decodedPossInstructionsList = {}; + std::vector decodedActionsList = {}; + size_t first_semicolon_pos = encodedLists.find(";"); + size_t second_semicolon_pos = encodedLists.find(";", first_semicolon_pos+1); + std::string unseparated_instruction_list = encodedLists.substr(first_semicolon_pos+1, second_semicolon_pos-first_semicolon_pos+1); + std::string unseparated_action_list = encodedLists.substr(second_semicolon_pos+1); + + // Load the list of possible instructions + size_t period_pos = unseparated_instruction_list.find("."); + start_pos = 0; + while (period_pos != std::string::npos) { + decodedPossInstructionsList.push_back(unseparated_instruction_list.substr(start_pos, period_pos)); + + start_pos = period_pos + 1; + period_pos = encodedLists.find(".", start_pos); + } + + // Load the list of actions + period_pos = unseparated_action_list.find("."); + start_pos = 0; + while (period_pos != std::string::npos) { + decodedActionsList.push_back(unseparated_action_list.substr(start_pos, period_pos)); + + start_pos = period_pos + 1; + period_pos = encodedLists.find(".", start_pos); + } + + instructionsList = decodedInstructionsList; + possibleInstructionsList = decodedPossInstructionsList; + actionsList = decodedActionsList; + resultsList.clear(); + resultsList.resize(LISTSIZE); + currentInstructionIndex = 0; + } + + /// @brief Print the agent void PrintAgent() override { - for (auto i = 0; i < LISTSIZE; i++) - { - std::cout << std::get<0>(instructionsList[i]) << " "; - } - std::cout << std::endl; + for (auto i = 0; i < LISTSIZE; i++) + { + std::cout << std::get<0>(instructionsList[i]) << " "; + } + std::cout << std::endl; } }; -} \ No newline at end of file +} From c4eba95b738fb0e83092a2e9813cb94940eaf9fa Mon Sep 17 00:00:00 2001 From: Aman Dhruva Thamminana <48414198+amantham20@users.noreply.github.com> Date: Wed, 6 Dec 2023 04:45:14 -0500 Subject: [PATCH 14/21] runner class for gp --- CMakeLists.txt | 15 ++++ source/gp_selective_runner_main.cpp | 108 ++++++++++++++++++++++++++++ source/gp_train_main.cpp | 7 +- 3 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 source/gp_selective_runner_main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bba2f19..823a4a60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,12 +86,27 @@ if (BUILD_GP_ONLY) #added the executable add_executable(gp_train_main source/gp_train_main.cpp) + add_executable(gp_selective_runner_main source/gp_selective_runner_main.cpp) + + target_sources(gp_selective_runner_main PRIVATE source/core/Entity.cpp) + target_include_directories(gp_selective_runner_main + PRIVATE ${CMAKE_SOURCE_DIR}/source/core + ${CMAKE_SOURCE_DIR}/source/Agents + ) + + target_link_libraries(gp_selective_runner_main + PRIVATE tinyxml2 + PRIVATE pthread + ) + #linking the targets target_sources(gp_train_main PRIVATE source/core/Entity.cpp) target_include_directories(gp_train_main PRIVATE ${CMAKE_SOURCE_DIR}/source/core ${CMAKE_SOURCE_DIR}/source/Agents ) + + target_link_libraries(gp_train_main PRIVATE tinyxml2 PRIVATE pthread diff --git a/source/gp_selective_runner_main.cpp b/source/gp_selective_runner_main.cpp new file mode 100644 index 00000000..9eb7a1b3 --- /dev/null +++ b/source/gp_selective_runner_main.cpp @@ -0,0 +1,108 @@ +/** + * This file is part of the Fall 2023, CSE 491 course project. + * @brief A simplistic main file to demonstrate a system. + * @note Status: PROPOSAL + **/ + +// Include the modules that we will be using. +#include "Agents/PacingAgent.hpp" +#include "Interfaces/TrashInterface.hpp" +#include "Worlds/MazeWorld.hpp" + +#include "Agents/GP/GPAgentBase.hpp" +#include "Agents/GP/LGPAgent.hpp" +#include "Agents/GP/CGPAgent.hpp" + +#include + + +void Printgrid(const cse491::MazeWorld &mazeWorld, char symbol = 'S', std::vector agents = {}) { + + + size_t arena = 0; + auto &grid = mazeWorld.GetGrid(arena); + std::vector symbol_grid(grid.GetHeight()); + + + + + + const auto &type_options = mazeWorld.GetCellTypes(); + // Load the world into the symbol_grid; + for (size_t y = 0; y < grid.GetHeight(); ++y) { + symbol_grid[y].resize(grid.GetWidth()); + for (size_t x = 0; x < grid.GetWidth(); ++x) { + symbol_grid[y][x] = type_options[grid.At(x, y)].symbol; + } + } + + + // Load the agents into the symbol_grid; + for (auto agent : agents) { + auto position = agent->GetPosition(); + symbol_grid[position.GetY()][position.GetX()] = symbol; + } + + std::cout << " "; + for (size_t x = 0; x < grid.GetWidth(); ++x) { + if (x % 10 == 0 && x != 0) { + std::cout << x / 10; // Print the ten's place of the column number + } else { + std::cout << " "; // Space for non-marker columns + } + } + std::cout << "\n"; + + // Print column numbers + std::cout << " "; // Space for row numbers + for (size_t x = 0; x < grid.GetWidth(); ++x) { + std::cout << x % 10; // Print only the last digit of the column number + } + std::cout << "\n"; + + // Print out the symbol_grid with a box around it. + std::cout << " +" << std::string(grid.GetWidth(), '-') << "+\n"; + for (size_t y = 0; y < grid.GetHeight(); ++y) { + + if (y % 10 == 0 && y != 0) { + std::cout << y / 10 << " "; // Print the ten's place of the row number + } else { + std::cout << " "; // Space for non-marker rows + } + + // Print row number + std::cout << y % 10 << "|"; // Print only the last digit of the row number + for (char cell: symbol_grid[y]) { + std::cout << cell; + } + std::cout << "|\n"; + } + + std::cout << " +" << std::string(grid.GetWidth(), '-') << "+\n"; + std::cout << std::endl; +} + + +int main() { + cse491::MazeWorld world; +/** + + 6,4,3,9,6;0.j.606uA3:0.0.61duDu:0.3.600:0.0.60iLO:0.5.602j4n:0.n.603vSU:0.6.614Prx:0.g.61xFL:0.l.602iIM:0.h.60185j:00w.3.619bwS:40g.n.619V/2:0.3.611Z3a:2w0.k.601p/w:40g.l.61iZK:0.m.602U29:0.0.601RtI:1g0.8.603nKR:0.4.604iMQ:0.m.61g6tL:0.j.602df1:a8j8.3.61bYRZ:0.f.612vfs:a084.3.6114Xe:0A60.e.614pTw:0.c.613CKA:0.j.611NQG:0.3.60765e:000e1w.f.602oxB:00085g.l.601xVk:000i1c.m.600: + + */ + + //GP agent + auto& GPagent = static_cast(world.AddAgent("GP 1").SetProperty("symbol", 'G')); + GPagent.Import("6,4,3,9,6;0.j.606uA3:0.0.61duDu:0.3.600:0.0.60iLO:0.5.602j4n:0.n.603vSU:0.6.614Prx:0.g.61xFL:0.l.602iIM:0.h.60185j:00w.3.619bwS:40g.n.619V/2:0.3.611Z3a:2w0.k.601p/w:40g.l.61iZK:0.m.602U29:0.0.601RtI:1g0.8.603nKR:0.4.604iMQ:0.m.61g6tL:0.j.602df1:a8j8.3.61bYRZ:0.f.612vfs:a084.3.6114Xe:0A60.e.614pTw:0.c.613CKA:0.j.611NQG:0.3.60765e:000e1w.f.602oxB:00085g.l.601xVk:000i1c.m.600:"); + GPagent.SetPosition(0, 1); + for (int i = 0; i < 100; ++i) { + world.RunAgents(); + + world.UpdateWorld(); + std::cout << "Generation: " << i << std::endl; + Printgrid(world, 'G', {&GPagent}); + + usleep(170000); + } + +} diff --git a/source/gp_train_main.cpp b/source/gp_train_main.cpp index 2544b48d..ee376e5f 100644 --- a/source/gp_train_main.cpp +++ b/source/gp_train_main.cpp @@ -15,6 +15,9 @@ int main() { const int num_threads = std::thread::hardware_concurrency(); + +// const int num_threads = 6; + std::cout << "Number of threads: " << num_threads << std::endl; @@ -23,17 +26,17 @@ int main() { cowboys::GPTrainingLoop loop; + loop.Initialize(100, 1000); loop.Run(11, 100, num_threads, true); // } + auto end_time = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end_time - start_time); auto seconds = duration.count() / 1000000.0; std::cout << "Time taken by Training: " << seconds << " seconds" << std::endl; - - return 0; } \ No newline at end of file From 97b5ac25d79926c4e143597ca9d8db6bebba3fcc Mon Sep 17 00:00:00 2001 From: Aman Dhruva Thamminana <48414198+amantham20@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:16:15 -0500 Subject: [PATCH 15/21] fixed all them errors --- source/Agents/GP/CGPAgent.hpp | 2 +- source/Agents/GP/GPAgentBase.hpp | 4 ++-- source/Agents/GP/GPTrainingLoop.hpp | 2 +- source/Agents/GP/LGPAgent.hpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/Agents/GP/CGPAgent.hpp b/source/Agents/GP/CGPAgent.hpp index 9063c554..288617a9 100644 --- a/source/Agents/GP/CGPAgent.hpp +++ b/source/Agents/GP/CGPAgent.hpp @@ -88,7 +88,7 @@ namespace cowboys { /// @param doc The XML document to serialize to. /// @param parentElem The parent element to serialize to. /// @param fitness The fitness of this agent to write to the XML. - void Serialize(tinyxml2::XMLDocument &doc, tinyxml2::XMLElement *parentElem, double fitness = -1) override { + void SerializeGP(tinyxml2::XMLDocument &doc, tinyxml2::XMLElement *parentElem, double fitness = -1) override { auto agentElem = doc.NewElement("CGPAgent"); parentElem->InsertEndChild(agentElem); diff --git a/source/Agents/GP/GPAgentBase.hpp b/source/Agents/GP/GPAgentBase.hpp index 88825ebf..3ce9fb8d 100644 --- a/source/Agents/GP/GPAgentBase.hpp +++ b/source/Agents/GP/GPAgentBase.hpp @@ -63,7 +63,7 @@ namespace cowboys { }; - virtual void Serialize(tinyxml2::XMLDocument &doc, tinyxml2::XMLElement *parentElem, double fitness = -1) = 0; + virtual void SerializeGP(tinyxml2::XMLDocument &doc, tinyxml2::XMLElement *parentElem, double fitness = -1) = 0; /// TODO: Might have to remove it virtual std::string Export() { return ""; } @@ -71,7 +71,7 @@ namespace cowboys { virtual void Reset(bool /*hard*/ = false) { extra_state["previous_action"] = 0; }; // virtual void crossover(const GPAgentBase &other) {}; - virtual void Import(const std::string &genotype) {}; + virtual void Import(const std::string &genotype) = 0; // -- Random Number Generation -- diff --git a/source/Agents/GP/GPTrainingLoop.hpp b/source/Agents/GP/GPTrainingLoop.hpp index d18103fc..b3ddd9e3 100644 --- a/source/Agents/GP/GPTrainingLoop.hpp +++ b/source/Agents/GP/GPTrainingLoop.hpp @@ -780,7 +780,7 @@ namespace cowboys { for (size_t i = 0; i < std::min(sortedAgents.size(), topN); ++i) { auto [arenaIDX, agentIDX] = sortedAgents[i]; - agents[arenaIDX][agentIDX]->Serialize(paramDocument, generationTag, TEMPAgentFitness[arenaIDX][agentIDX]); + agents[arenaIDX][agentIDX]->SerializeGP(paramDocument, generationTag, TEMPAgentFitness[arenaIDX][agentIDX]); } diff --git a/source/Agents/GP/LGPAgent.hpp b/source/Agents/GP/LGPAgent.hpp index 5905ffeb..6b09f5b9 100644 --- a/source/Agents/GP/LGPAgent.hpp +++ b/source/Agents/GP/LGPAgent.hpp @@ -285,7 +285,7 @@ namespace cowboys /// @param doc The XML document to serialize to. /// @param parentElem The parent element to serialize to. /// @param fitness The fitness of this agent to write to the XML. - void Serialize(tinyxml2::XMLDocument & doc, tinyxml2::XMLElement* parentElem, double fitness = -1) override + void SerializeGP(tinyxml2::XMLDocument & doc, tinyxml2::XMLElement* parentElem, double fitness = -1) override { auto agentElem = doc.NewElement("LGPAgent"); parentElem->InsertEndChild(agentElem); @@ -299,7 +299,7 @@ namespace cowboys /// @brief Load in the string representation of an LGP agent and configure this agent based on it. /// @param genotype The string representation of an LGP agent. - void Import(std::string encodedLists) { + void Import(const std::string & encodedLists) override { std::vector> decodedInstructionsList = {}; std::string decodedInstruction; size_t start_pos = 0; From 4e1c13d16bab43e3ff9ec4d8d3f477bb9c060632 Mon Sep 17 00:00:00 2001 From: Rajmeet Singh Chandok Date: Wed, 6 Dec 2023 14:19:22 -0500 Subject: [PATCH 16/21] GPAnalyzer Created --- source/Agents/GP/GPAgentAnalyze.h | 46 +++++++++++++++++++++++++++-- source/Agents/GP/GPTrainingLoop.hpp | 2 +- source/gp_train_main.cpp | 4 +-- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/source/Agents/GP/GPAgentAnalyze.h b/source/Agents/GP/GPAgentAnalyze.h index db31793e..bcf3569b 100644 --- a/source/Agents/GP/GPAgentAnalyze.h +++ b/source/Agents/GP/GPAgentAnalyze.h @@ -1,5 +1,6 @@ // -// Created by rajme on 12/4/2023. +// A class that analyzes the data of the best agent in the GP algorithm +// and saves it to a csv file // #pragma once @@ -13,35 +14,75 @@ namespace cowboys { class GPAgentAnalyzer { private: + /// The average fitness of the best agent std::vector average_fitness; + + /// The max fitness of the best agent std::vector max_fitness; + + /// The weighted score of the best agent std::vector elite_score; + + /// The average score of the best agent std::vector average_score; + + /// The number of agents with the max fitness std::vector max_agents; public: + /** + * @brief Construct a new GP Agent Analyzer object + */ GPAgentAnalyzer() = default; + + /** + * @brief Destroy the GP Agent Analyzer object + */ ~GPAgentAnalyzer() = default; + /** + * @brief Adds the average fitness of the best agent + * @param fitness + */ void addAverageFitness(double fitness) { average_fitness.push_back(fitness); } + /** + * @brief Adds the max fitness of the best agent + * @param fitness + */ void addMaxFitness(double fitness) { max_fitness.push_back(fitness); } + /** + * @brief Adds the weighted score of the best agent + * @param score + */ void addEliteScore(double score) { elite_score.push_back(score); } + /** + * @brief Adds the average score of the best agent + * @param score + */ void addAverageScore(double score) { average_score.push_back(score); } + + /** + * @brief Adds the number of agents with the max fitness + * @param num_agents + */ void addNumAgentsWithMaxFitness(double num_agents) { max_agents.push_back(num_agents); } + /** + * @brief Saves the data to a csv file + */ void saveToFile() { // create a new file std::ofstream file("gp_agent_analyzer.csv"); @@ -52,11 +93,10 @@ namespace cowboys { for (int i = 0; i < average_fitness.size(); i++) { file << average_fitness[i] << "," << max_fitness[i] << "," << elite_score[i] << "," << average_score[i] << "," << max_agents[i] << "\n"; } + std::cout << "Saved GP Agent Analyzer data to gp_agent_analyzer.csv" << std::endl; // close the file file.close(); - - } }; diff --git a/source/Agents/GP/GPTrainingLoop.hpp b/source/Agents/GP/GPTrainingLoop.hpp index 12f9b8d0..6eb3827a 100644 --- a/source/Agents/GP/GPTrainingLoop.hpp +++ b/source/Agents/GP/GPTrainingLoop.hpp @@ -26,7 +26,7 @@ namespace cowboys { - constexpr unsigned int TRAINING_SEED = 10; ///< If this is 0, then a random seed will be used + constexpr unsigned int TRAINING_SEED = 0; ///< If this is 0, then a random seed will be used template class GPTrainingLoop { diff --git a/source/gp_train_main.cpp b/source/gp_train_main.cpp index bc7f7384..16493588 100644 --- a/source/gp_train_main.cpp +++ b/source/gp_train_main.cpp @@ -24,8 +24,8 @@ int main() { cowboys::GPTrainingLoop loop(false); - loop.Initialize(50, 200); - loop.Run(80, 50, num_threads, false); + loop.Initialize(16, 1000); + loop.Run(150, 70, num_threads, false); auto end_time = std::chrono::high_resolution_clock::now(); From c564f5070e5e26e84e6aeec9811ce583251beb06 Mon Sep 17 00:00:00 2001 From: ssit Date: Wed, 6 Dec 2023 15:08:29 -0500 Subject: [PATCH 17/21] Fix locale::facet::_S_create_c_locale name not valid --- source/Agents/GP/GPTrainingLoop.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Agents/GP/GPTrainingLoop.hpp b/source/Agents/GP/GPTrainingLoop.hpp index d640aaaf..b46681e6 100644 --- a/source/Agents/GP/GPTrainingLoop.hpp +++ b/source/Agents/GP/GPTrainingLoop.hpp @@ -162,7 +162,7 @@ namespace cowboys { const size_t numAgents = numArenas * NumAgentsForArena; std::stringstream ss; - ss.imbue(std::locale("")); + // ss.imbue(std::locale("")); ss << std::fixed << numAgents; std::cout << "number of agents " << std::fixed << ss.str() << std::endl; From a1732f499f68e61064301f5728254cb283447c01 Mon Sep 17 00:00:00 2001 From: ssit Date: Wed, 6 Dec 2023 15:09:07 -0500 Subject: [PATCH 18/21] Remove random number generator from CGPGenotype --- source/Agents/GP/CGPAgent.hpp | 2 -- source/Agents/GP/CGPGenotype.hpp | 27 +++++++-------------------- tests/unit/Agents/GP/GraphBuilder.cpp | 4 ++-- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/source/Agents/GP/CGPAgent.hpp b/source/Agents/GP/CGPAgent.hpp index 288617a9..d50c5457 100644 --- a/source/Agents/GP/CGPAgent.hpp +++ b/source/Agents/GP/CGPAgent.hpp @@ -63,8 +63,6 @@ namespace cowboys { genotype = CGPGenotype({INPUT_SIZE, action_map.size(), NUM_LAYERS, NUM_NODES_PER_LAYER, LAYERS_BACK}); } - genotype.SetSeed(rand()); - // Mutate the beginning genotype, might not want this. MutateAgent(0.2); diff --git a/source/Agents/GP/CGPGenotype.hpp b/source/Agents/GP/CGPGenotype.hpp index cc146244..954fdc95 100644 --- a/source/Agents/GP/CGPGenotype.hpp +++ b/source/Agents/GP/CGPGenotype.hpp @@ -232,9 +232,6 @@ namespace cowboys { /// The node configurations. std::vector nodes; - /// The random number generator. - std::mt19937 rng; - private: /// @brief Encodes the header into a string. /// @return The encoded header. @@ -512,11 +509,8 @@ namespace cowboys { /// @return Bool value to indicate if any input connections non-zero. bool HasInputConnections() const { for (auto it = begin(); it != end(); ++it) { - if (std::any_of( - it->input_connections.begin(), - it->input_connections.end(), - [](char c) { return c != '0'; } - )) return true; + if (std::any_of(it->input_connections.begin(), it->input_connections.end(), [](char c) { return c != '0'; })) + return true; } return false; } @@ -563,22 +557,15 @@ namespace cowboys { return header + HEADER_END + genotype; } - /// @brief Sets the seed of the random number generator. - CGPGenotype &SetSeed(size_t seed) { - rng.seed(seed); - return *this; - } - /// @brief Mutates the genotype. /// @param mutation_rate Value between 0 and 1 representing the probability of mutating a value. /// @param mutation The function to use for mutating the output. The function will receive the node gene as a /// parameter. /// @return This genotype. - CGPGenotype &Mutate(double mutation_rate, std::function mutation) { + CGPGenotype &Mutate(double mutation_rate, GPAgentBase &agent, std::function mutation) { assert(mutation_rate >= 0.0 && mutation_rate <= 1.0); - std::uniform_real_distribution dist_mutation(0.0, 1.0); for (CGPNodeGene &node : nodes) - if (dist_mutation(rng) < mutation_rate) + if (agent.GetRandom() < mutation_rate) mutation(node); return *this; } @@ -590,7 +577,7 @@ namespace cowboys { /// @return This genotype. CGPGenotype &MutateConnections(double mutation_rate, GPAgentBase &agent) { std::uniform_int_distribution dist(0, 1); - Mutate(mutation_rate, [&agent](CGPNodeGene &node) { + Mutate(mutation_rate, agent, [&agent](CGPNodeGene &node) { for (char &con : node.input_connections) { con = agent.GetRandomULL(2) == 0 ? '0' : '1'; } @@ -603,7 +590,7 @@ namespace cowboys { /// @param num_functions The number of functions available to the nodes. /// @return This genotype. CGPGenotype &MutateFunctions(double mutation_rate, size_t num_functions, GPAgentBase &agent) { - Mutate(mutation_rate, + Mutate(mutation_rate, agent, [num_functions, &agent](CGPNodeGene &node) { node.function_idx = agent.GetRandomULL(num_functions); }); return *this; } @@ -615,7 +602,7 @@ namespace cowboys { /// @return This genotype. CGPGenotype &MutateOutputs(double mutation_rate, double mean, double std, GPAgentBase &agent, bool additive = true) { - Mutate(mutation_rate, [mean, std, &agent, additive](CGPNodeGene &node) { + Mutate(mutation_rate, agent, [mean, std, &agent, additive](CGPNodeGene &node) { double mutation = agent.GetRandomNormal(mean, std); if (additive) { node.default_output += mutation; diff --git a/tests/unit/Agents/GP/GraphBuilder.cpp b/tests/unit/Agents/GP/GraphBuilder.cpp index c3f3d06c..4c227c28 100644 --- a/tests/unit/Agents/GP/GraphBuilder.cpp +++ b/tests/unit/Agents/GP/GraphBuilder.cpp @@ -50,7 +50,7 @@ TEST_CASE("Cartesian Graph", "[group7][graph][cartesian]") { size_t iterations = 100; for (size_t i = 0; i < iterations; ++i) { CGPGenotype genotype({INPUT_SIZE, NUM_OUTPUTS, NUM_LAYERS, NUM_NODES_PER_LAYER, LAYERS_BACK}); - genotype.SetSeed(i).MutateDefault(1, mock_agent, NODE_FUNCTION_SET.size()); + genotype.MutateDefault(1, mock_agent, NODE_FUNCTION_SET.size()); auto graph = builder.CartesianGraph(genotype, NODE_FUNCTION_SET); auto new_action = graph->MakeDecision(inputs, actions); choose_same_action = choose_same_action && (new_action == action); @@ -75,7 +75,7 @@ TEST_CASE("Cartesian Graph", "[group7][graph][cartesian]") { size_t iterations = 100; for (size_t i = 0; i < iterations; ++i) { auto copy = base; - copy.SetSeed(i).MutateHeader(1, mock_agent); + copy.MutateHeader(1, mock_agent); CHECK_FALSE(copy == base); auto expanded_graph = builder.CartesianGraph(copy, NODE_FUNCTION_SET); auto new_action = expanded_graph->MakeDecision(inputs, actions); From 93518e2bc27d05f78ec7787dde2bc60a1bc8443a Mon Sep 17 00:00:00 2001 From: ssit Date: Wed, 6 Dec 2023 15:12:14 -0500 Subject: [PATCH 19/21] Remove status --- source/Agents/GP/CGPAgent.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/source/Agents/GP/CGPAgent.hpp b/source/Agents/GP/CGPAgent.hpp index d50c5457..27c6964c 100644 --- a/source/Agents/GP/CGPAgent.hpp +++ b/source/Agents/GP/CGPAgent.hpp @@ -1,7 +1,6 @@ /** * This file is part of the Fall 2023, CSE 491 course project. * @brief An Agent based on genetic programming. - * @note Status: PROPOSAL **/ #pragma once From 370f344f3da4b1ccfeed268f4da96ea7af004758 Mon Sep 17 00:00:00 2001 From: ssit Date: Thu, 7 Dec 2023 02:38:43 -0500 Subject: [PATCH 20/21] Add start pos, add A* distance to start, switch fitness to average --- source/Agents/GP/CGPAgent.hpp | 4 ++-- source/Agents/GP/GPAgentBase.hpp | 21 +++++++++++++++++---- source/Agents/GP/GPTrainingLoop.hpp | 8 ++++++-- source/Agents/GP/Graph.hpp | 10 ++++++++-- source/Agents/GP/GraphNode.hpp | 8 ++++++++ tests/unit/Agents/GP/CGPAgent.cpp | 2 +- 6 files changed, 42 insertions(+), 11 deletions(-) diff --git a/source/Agents/GP/CGPAgent.hpp b/source/Agents/GP/CGPAgent.hpp index 27c6964c..26e1f661 100644 --- a/source/Agents/GP/CGPAgent.hpp +++ b/source/Agents/GP/CGPAgent.hpp @@ -16,13 +16,13 @@ namespace cowboys { /// Don't know the maximum size a state can be, arbitrary large number - constexpr size_t INPUT_SIZE = 6; + constexpr size_t INPUT_SIZE = 9; /// Number of computational layers for each agent constexpr size_t NUM_LAYERS = 3; /// The number of nodes in each layer - constexpr size_t NUM_NODES_PER_LAYER = 5; + constexpr size_t NUM_NODES_PER_LAYER = 2; /// The number of layers preceding a node's layer that the node can reference constexpr size_t LAYERS_BACK = 2; diff --git a/source/Agents/GP/GPAgentBase.hpp b/source/Agents/GP/GPAgentBase.hpp index 3ce9fb8d..7a79d9c1 100644 --- a/source/Agents/GP/GPAgentBase.hpp +++ b/source/Agents/GP/GPAgentBase.hpp @@ -26,19 +26,28 @@ namespace cowboys { std::normal_distribution norm_dist; ///< Normal distribution. public: - GPAgentBase(size_t id, const std::string &name) : AgentBase(id, name) { extra_state["previous_action"] = 0; } + GPAgentBase(size_t id, const std::string &name) : AgentBase(id, name) { + Reset(); + } ~GPAgentBase() = default; /// @brief Setup graph. /// @return Success. - bool Initialize() override { return true; } + bool Initialize() override { return true; } /// Choose the action to take a step in the appropriate direction. size_t SelectAction(const cse491::WorldGrid &grid, const cse491::type_options_t &type_options, const cse491::item_map_t &item_set, const cse491::agent_map_t &agent_set) override { + // Update extra state information before action + if (extra_state["starting_x"] == std::numeric_limits::max()) { + auto pos = GetPosition(); + extra_state["starting_x"] = pos.GetX(); + extra_state["starting_y"] = pos.GetY(); + } + size_t action = GetAction(grid, type_options, item_set, agent_set); - // Update extra state information. + // Update extra state information after action extra_state["previous_action"] = action; return action; @@ -68,7 +77,11 @@ namespace cowboys { /// TODO: Might have to remove it virtual std::string Export() { return ""; } - virtual void Reset(bool /*hard*/ = false) { extra_state["previous_action"] = 0; }; + virtual void Reset(bool /*hard*/ = false) { + extra_state["previous_action"] = 0; + extra_state["starting_x"] = std::numeric_limits::max(); + extra_state["starting_y"] = std::numeric_limits::max(); + }; // virtual void crossover(const GPAgentBase &other) {}; virtual void Import(const std::string &genotype) = 0; diff --git a/source/Agents/GP/GPTrainingLoop.hpp b/source/Agents/GP/GPTrainingLoop.hpp index b46681e6..ff9d3618 100644 --- a/source/Agents/GP/GPTrainingLoop.hpp +++ b/source/Agents/GP/GPTrainingLoop.hpp @@ -1040,6 +1040,9 @@ namespace cowboys { void RunArena(size_t arena, size_t numberOfTurns) { for (size_t startPos_idx = 0; startPos_idx < STARTPOSITIONS.size(); ++startPos_idx) { for(size_t a = 0; a < agents[arena].size(); ++a) { + // Reset the agent before each run + agents[arena][a]->Reset(); + // Set the starting position agents[arena][a]->SetPosition(STARTPOSITIONS[startPos_idx]); } @@ -1074,8 +1077,9 @@ namespace cowboys { // TEMPAgentFitness[arena][a] += computeMedian(); double min = *std::min_element(scores.begin(), scores.end()); [[maybe_unused]] double avg = TEMPAgentFitness[arena][a] / STARTPOSITIONS.size(); -// TEMPAgentFitness[arena][a] = 0.7 * min + 0.3 * avg; - TEMPAgentFitness[arena][a] = min; + // TEMPAgentFitness[arena][a] = 0.7 * min + 0.3 * avg; + // TEMPAgentFitness[arena][a] = min; + TEMPAgentFitness[arena][a] = avg; } } diff --git a/source/Agents/GP/Graph.hpp b/source/Agents/GP/Graph.hpp index 458710ba..4580ab7f 100644 --- a/source/Agents/GP/Graph.hpp +++ b/source/Agents/GP/Graph.hpp @@ -8,6 +8,7 @@ #include #include "../../core/AgentBase.hpp" +#include "../AgentLibary.hpp" #include "GraphNode.hpp" namespace cowboys { @@ -115,7 +116,7 @@ namespace cowboys { /// @return A vector of doubles for the decision graph. std::vector EncodeState(const cse491::WorldGrid &grid, const cse491::type_options_t & /*type_options*/, const cse491::item_map_t & /*item_set*/, const cse491::agent_map_t & /*agent_set*/, - const cse491::Entity *agent, + const cse491::AgentBase *agent, const std::unordered_map &extra_agent_state) { /// TODO: Implement this function properly. std::vector inputs; @@ -129,8 +130,13 @@ namespace cowboys { double right_state = grid.IsValid(current_position.ToRight()) ? grid.At(current_position.ToRight()) : 0.; double prev_action = extra_agent_state.at("previous_action"); + double starting_x = extra_agent_state.at("starting_x"); + double starting_y = extra_agent_state.at("starting_y"); + auto starting_pos = cse491::GridPosition(starting_x, starting_y); + auto path = walle::GetShortestPath(agent->GetPosition(), starting_pos, agent->GetWorld(), *agent); + double distance_from_start = path.size(); - inputs.insert(inputs.end(), {prev_action, current_state, above_state, below_state, left_state, right_state}); + inputs.insert(inputs.end(), {prev_action, starting_x, starting_y, distance_from_start, current_state, above_state, below_state, left_state, right_state}); return inputs; } diff --git a/source/Agents/GP/GraphNode.hpp b/source/Agents/GP/GraphNode.hpp index 3aa213a5..93327dac 100644 --- a/source/Agents/GP/GraphNode.hpp +++ b/source/Agents/GP/GraphNode.hpp @@ -387,6 +387,14 @@ namespace cowboys { /// @param agent The agent that the node belongs to. /// @return The distance to the grid position using A* double AStarDistance(const GraphNode &node, const cse491::AgentBase &agent) { + // + // The outputs of the first two connections are the x and y coordinates of the goal position. It'd probably be rare + // for agents to randomly use it in a useful way. Most of the time when it IS used, there is no input connections + // and thus the default output is used, so it isn't REALLY being used. Other times when it does have input + // connections, the agent has a lower fitness, so it probably wasn't making good use of it. + // + // Decided to make an easier way A* can be used by agents by giving the A* distance from the agent's start position + // as an input. This can still be used in the off chance it is useful. auto vals = node.GetInputValues<2>(std::array{0, 1}); if (!vals.has_value()) return node.GetDefaultOutput(); diff --git a/tests/unit/Agents/GP/CGPAgent.cpp b/tests/unit/Agents/GP/CGPAgent.cpp index f28b8578..648cd5c6 100644 --- a/tests/unit/Agents/GP/CGPAgent.cpp +++ b/tests/unit/Agents/GP/CGPAgent.cpp @@ -13,7 +13,7 @@ using namespace cowboys; struct MockWorld : cse491::WorldBase { - int DoAction(cse491::AgentBase &agent, size_t action_id) { return 0; } + int DoAction(cse491::AgentBase &, size_t) { return 0; } }; MockWorld world; From fcf9a72f89cf1960cab1e56faff6f7132789f614 Mon Sep 17 00:00:00 2001 From: ssit Date: Thu, 7 Dec 2023 03:19:29 -0500 Subject: [PATCH 21/21] Code reuse for complexity and prevent graphs from blowing up in size --- source/Agents/GP/CGPAgent.hpp | 15 +++++++++++++++ source/Agents/GP/GPTrainingLoop.hpp | 20 ++------------------ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/source/Agents/GP/CGPAgent.hpp b/source/Agents/GP/CGPAgent.hpp index 26e1f661..b97881e7 100644 --- a/source/Agents/GP/CGPAgent.hpp +++ b/source/Agents/GP/CGPAgent.hpp @@ -127,6 +127,21 @@ namespace cowboys { assert(dynamic_cast(&other) != nullptr); Configure(dynamic_cast(other)); } + + /// @brief The complexity of this agent. Used for fitness. + /// @return The complexity of this agent. + double GetComplexity() const { + double connection_complexity = + static_cast(genotype.GetNumConnections()) / genotype.GetNumPossibleConnections(); + + double functional_nodes = genotype.GetNumFunctionalNodes(); + + // Just needed some function such that connection_complexity + node_complexity grows as the number of nodes grows, this function makes the increase more gradual. + double node_complexity = std::log(functional_nodes) / 5; + + double complexity = connection_complexity + node_complexity; + return complexity; + } }; } // End of namespace cowboys diff --git a/source/Agents/GP/GPTrainingLoop.hpp b/source/Agents/GP/GPTrainingLoop.hpp index ff9d3618..896ddfe3 100644 --- a/source/Agents/GP/GPTrainingLoop.hpp +++ b/source/Agents/GP/GPTrainingLoop.hpp @@ -283,15 +283,7 @@ namespace cowboys { // Agent complexity, temporarily doing this in a bad way if (auto *cgp = dynamic_cast(&agent)) { - auto genotype = cgp->GetGenotype(); - double connection_complexity = - static_cast(genotype.GetNumConnections()) / genotype.GetNumPossibleConnections(); - - double functional_nodes = genotype.GetNumFunctionalNodes(); - double node_complexity = functional_nodes / (functional_nodes + 1); - - double complexity = connection_complexity + node_complexity; - fitness -= complexity; + fitness -= cgp->GetComplexity(); } return fitness; @@ -316,15 +308,7 @@ namespace cowboys { cse491::AgentBase &agent = *agents[arena][a]; // Agent complexity, temporarily doing this in a bad way if (auto *cgp = dynamic_cast(&agent)){ - auto genotype = cgp->GetGenotype(); - double connection_complexity = - static_cast(genotype.GetNumConnections()) / genotype.GetNumPossibleConnections(); - - double functional_nodes = genotype.GetNumFunctionalNodes(); - double node_complexity = functional_nodes / (functional_nodes + 1); - - double complexity = connection_complexity + node_complexity; - fitness -= complexity; + fitness -= cgp->GetComplexity(); } return fitness;