Skip to content

Commit

Permalink
Merge remote-tracking branch 'group_7/GPLoop-amanXSimon-CGPA' into de…
Browse files Browse the repository at this point in the history
…velopment
  • Loading branch information
FergusonAJ committed Dec 8, 2023
2 parents 2ac181f + fcf9a72 commit 33ed601
Show file tree
Hide file tree
Showing 16 changed files with 770 additions and 111 deletions.
29 changes: 16 additions & 13 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,24 +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)

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

# Common libraries to link with
set(COMMON_LIBRARIES tinyxml2 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 ()

Expand Down
39 changes: 32 additions & 7 deletions source/Agents/GP/CGPAgent.hpp
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -17,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;
Expand Down Expand Up @@ -65,8 +64,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);

Expand All @@ -80,8 +77,11 @@ namespace cowboys {
return action_to_take;
}


void Serialize(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* parentElem, double fitness = -1) override {
/// @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 SerializeGP(tinyxml2::XMLDocument &doc, tinyxml2::XMLElement *parentElem, double fitness = -1) override {
auto agentElem = doc.NewElement("CGPAgent");
parentElem->InsertEndChild(agentElem);

Expand All @@ -96,6 +96,16 @@ namespace cowboys {

}

/// @brief Export the genotype for this agent.
/// @return The string representation of the genotype for this agent.
std::string Export() override { return genotype.Export(); }

/// @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(const std::string &genotype) override {
this->genotype.Configure(genotype);
decision_graph = GraphBuilder().CartesianGraph(this->genotype, FUNCTION_SET, this);
}

/// @brief Get the genotype for this agent.
/// @return A const reference to the genotype for this agent.
Expand All @@ -115,6 +125,21 @@ namespace cowboys {
assert(dynamic_cast<const CGPAgent *>(&other) != nullptr);
Configure(dynamic_cast<const CGPAgent &>(other));
}

/// @brief The complexity of this agent. Used for fitness.
/// @return The complexity of this agent.
double GetComplexity() const {
double connection_complexity =
static_cast<double>(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
27 changes: 7 additions & 20 deletions source/Agents/GP/CGPGenotype.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,9 +232,6 @@ namespace cowboys {
/// The node configurations.
std::vector<CGPNodeGene> nodes;

/// The random number generator.
std::mt19937 rng;

private:
/// @brief Encodes the header into a string.
/// @return The encoded header.
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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<void(CGPNodeGene &)> mutation) {
CGPGenotype &Mutate(double mutation_rate, GPAgentBase &agent, std::function<void(CGPNodeGene &)> mutation) {
assert(mutation_rate >= 0.0 && mutation_rate <= 1.0);
std::uniform_real_distribution<double> 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;
}
Expand All @@ -590,7 +577,7 @@ namespace cowboys {
/// @return This genotype.
CGPGenotype &MutateConnections(double mutation_rate, GPAgentBase &agent) {
std::uniform_int_distribution<size_t> 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';
}
Expand All @@ -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;
}
Expand All @@ -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;
Expand Down
104 changes: 104 additions & 0 deletions source/Agents/GP/GPAgentAnalyze.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//
// A class that analyzes the data of the best agent in the GP algorithm
// and saves it to a csv file
//

#pragma once

#include <vector>
#include <iostream>
#include <string>
#include <fstream>

namespace cowboys {

class GPAgentAnalyzer {
private:
/// The average fitness of the best agent
std::vector<double> average_fitness;

/// The max fitness of the best agent
std::vector<double> max_fitness;

/// The weighted score of the best agent
std::vector<double> elite_score;

/// The average score of the best agent
std::vector<double> average_score;

/// The number of agents with the max fitness
std::vector<double> 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");

// 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";
}
std::cout << "Saved GP Agent Analyzer data to gp_agent_analyzer.csv" << std::endl;

// close the file
file.close();
}
};

} // namespace cowboys

25 changes: 19 additions & 6 deletions source/Agents/GP/GPAgentBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,28 @@ namespace cowboys {
std::normal_distribution<double> 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<double>::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;
Expand All @@ -63,14 +72,18 @@ 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;

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<double>::max();
extra_state["starting_y"] = std::numeric_limits<double>::max();
};

// virtual void crossover(const GPAgentBase &other) {};
// virtual void Import(const std::string &genotype) {};
virtual void Import(const std::string &genotype) = 0;

// -- Random Number Generation --

Expand Down
Loading

0 comments on commit 33ed601

Please sign in to comment.