From c95f4737bbbe19892585e2ca9b0e647a782f00ba Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sat, 27 Feb 2021 15:44:25 -0500 Subject: [PATCH 01/19] Add QueueManager --- include/emp/prefab/QueueManager.hpp | 221 ++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 include/emp/prefab/QueueManager.hpp diff --git a/include/emp/prefab/QueueManager.hpp b/include/emp/prefab/QueueManager.hpp new file mode 100644 index 0000000000..8aeffe1148 --- /dev/null +++ b/include/emp/prefab/QueueManager.hpp @@ -0,0 +1,221 @@ +/** + * @note This file is part of Empirical, https://github.com/devosoft/Empirical + * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * @date 2020 + * + * @file queue-manager.h + * @brief A tool for processing multiple simulation runs onto a figure and table, displaying real-time statistics + * @note Status: + */ + +/// The goal of creating this tool is to alleviate the process of running multiple simulations of the same kind within +/// a queue structure. A user is able to visually see the results of their simulations, and each run held within the queue +/// runs subsequently after the other. Real-time statistics, that the user is able to display, are posted within a table as each run is carried out. +/// Here is a link to a blogpost that describes this tool's inpsiration, purpose, and required instructions: +/// (Link to blogpost) + +#pragma once + +#include +#include +#include +#include +#include + +#include "emp/base/vector.hpp" +#include "emp/config/SettingConfig.hpp" +#include "emp/math/Random.hpp" +#include "emp/math/math.hpp" +#include "emp/web/Div.hpp" +#include "emp/web/web.hpp" + +namespace emp { + +/// Information of each element within queue. This info represents the information required for each run to be processed. +struct RunInfo { + SettingConfig runinfo_config; + + size_t id; + + size_t cur_epoch; + size_t num_coop; + std::string num_defect; + + RunInfo(SettingConfig _config, size_t _id) + : runinfo_config(_config), id(_id), cur_epoch(0), num_coop(0), num_defect("") { ; } +}; + +/// Primary class that establishes queue for runs and processes them accordingly +class QueueManager { + private: + SettingConfig queue_config; + std::queue runs; + emp::web::Div display_div; + std::string table_id; + // ordered names of dependant headers with associated column #'s + emp::vector> ordered_names; + std::unordered_map> dependant_headers; + // for SimplePDWorld + size_t epoch_ = 0; + size_t coop_ = 0; + + public: + void SetEpoch(size_t epoch) { epoch_ = epoch; } + void SetNumCoop(size_t coop) { coop_ = coop; } + + /// Default constructor + QueueManager() = default; + + /// Config constructor + QueueManager(SettingConfig user_config) : queue_config(user_config) { ; } + + /// Checks if queue is empty + bool IsEmpty() { + return runs.empty(); + } + + /// Checks how runs are in the queue + size_t RunsRemaining() { + return runs.size(); + } + + /// Adds run to queue with run info for paramters + void AddRun(SettingConfig other) { + RunInfo new_run(other, runs.size()); + runs.push(new_run); + } + + /// Remove run from front of queue + void RemoveRun() { + emp_assert(!IsEmpty(), "Queue is empty! Cannot remove!"); + runs.pop(); + } + + /// Front Run Getter + RunInfo& FrontRun() { + emp_assert(!IsEmpty(), "Queue is empty! Cannot access Front!"); + return runs.front(); + } + + /// Returns this dic + emp::web::Div GetDiv() { + return display_div; + } + /// Clears the content of this div + void ResetDiv() { + display_div.Clear(); + } + + /// Initializes table to web + void DivAddTable(size_t row, size_t col, std::string id) { + table_id = id; + emp::web::Table result_tab(row, col, id); + result_tab.SetCSS("border-collapse", "collapse"); + result_tab.SetCSS("border", "3px solid black"); + result_tab.CellsCSS("border", "1px solid black"); + + result_tab.GetCell(0, 0).SetHeader() << "Run"; + int column_count = 1; + emp::vector setting_names = queue_config.GetSettingMapNames(); + for (const auto& p : setting_names) { + result_tab.GetCell(0, column_count).SetHeader() << "" << p << ""; + ++column_count; + } + + /* if adding more features after this point, keep in mind of where + the col count will be */ + for (auto& i : ordered_names) { + i.second = column_count; + result_tab.GetCell(0, column_count++).SetHeader() << i.first; + } + + display_div << result_tab; + } + + /// Extends table once button is clicked + void DivButtonTable(int run_id) { + emp::web::Table my_table = display_div.Find(table_id); + + // Update the table. + int line_id = my_table.GetNumRows(); + my_table.Rows(line_id + 1); + int col_count = 0; + my_table.GetCell(line_id, col_count) << run_id; + for (auto p : queue_config.GetSettingMapBase()) { + my_table.GetCell(line_id, ++col_count) << (*p).AsString(); + } + + for (int i = 0; i < dependant_headers.size(); i++) { + my_table.GetCell(line_id, ++col_count) << "Waiting..."; // world.GetE(); world.CountCoop(); (world.GetN() - world.CountCoop()); + } + + // Draw the new table. + my_table.CellsCSS("border", "1px solid black"); + my_table.Redraw(); + } + + /// Run info in table is updated + void DivInfoTable(size_t id, size_t cur_epoch, size_t num_coop, std::string num_defect) { + emp::web::Table my_table = display_div.Find(table_id); + my_table.Freeze(); + my_table.GetCell(id + 1, 5).ClearChildren() << cur_epoch; + my_table.GetCell(id + 1, 6).ClearChildren() << num_coop; + my_table.GetCell(id + 1, 7).ClearChildren() << num_defect; + my_table.Activate(); + } + + /// Calculations required for updating table + void DivTableCalc() { + size_t id = FrontRun().id; + size_t current_epoch = epoch_; + RunInfo& current_run = FrontRun(); + + current_run.cur_epoch = current_epoch; + current_run.num_coop = coop_; + // user function configuration + for (auto i : dependant_headers) { + if (i.first == "Num Defect") { + current_run.num_defect = (dependant_headers.begin()->second)(); + } + } + + if (current_epoch >= current_run.runinfo_config.GetValue("E_value")) { // Are we done with this run? + RemoveRun(); // Updates to the next run + } + + DivInfoTable(id, current_epoch, current_run.num_coop, current_run.num_defect); + } + + /// Creates area for user to input how many runs will be queued + size_t DivAddTextArea() { + size_t num_runs = 10; + emp::web::TextArea run_input([&num_runs](const std::string& str) { + num_runs = emp::from_string(str); + }, + "run_count"); + + run_input.SetText(emp::to_string(num_runs)); + display_div << run_input; + return num_runs; + } + + /// Creates queue button + void DivButton(size_t num_runs) { + emp::web::Button my_button([this, num_runs]() { + for (int run_id = 0; run_id < num_runs; run_id++) { + AddRun(queue_config); + DivButtonTable(run_id); + } + }, + "Queue", "queue_but"); + display_div << my_button; + } + + /// Adds dependant variables to tables + void AddDepVariable(std::function func, std::string header_name) { + ordered_names.push_back({header_name, 0}); + dependant_headers.insert({header_name, func}); + } +}; + +} // namespace emp From 0f3355011c208941da20dfc8748a6796cfb03bfc Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sat, 27 Feb 2021 18:59:22 -0500 Subject: [PATCH 02/19] Updates to queue manager --- include/emp/config/SettingConfig.hpp | 645 +++++++++++++++------------ include/emp/prefab/QueueManager.hpp | 59 +-- 2 files changed, 375 insertions(+), 329 deletions(-) diff --git a/include/emp/config/SettingConfig.hpp b/include/emp/config/SettingConfig.hpp index 39f1ec651d..c23d24622a 100644 --- a/include/emp/config/SettingConfig.hpp +++ b/include/emp/config/SettingConfig.hpp @@ -11,10 +11,10 @@ #ifndef EMP_SETTING_CONFIG_H #define EMP_SETTING_CONFIG_H -#include #include #include #include +#include #include "../base/Ptr.hpp" #include "../base/vector.hpp" @@ -26,111 +26,98 @@ namespace emp { - /// Class to take a set of value for each "setting" and then step through all combinations of - /// those values for a factorial analysis. +/// Class to take a set of value for each "setting" and then step through all combinations of +/// those values for a factorial analysis. - class SettingConfig { - private: +class SettingConfig { + private: /// Base class to describe information about a single setting. struct SettingBase { - std::string name; ///< Name for this setting - std::string desc; ///< Description of setting - char flag; ///< Command-line flag ('\0' for none) - std::string option; ///< Command-line longer option. - std::string args_label; ///< Label for option arguments (used in --help) - - SettingBase(const std::string & _name, const std::string & _desc, - const char _flag, const std::string & _args_label) - : name(_name), desc(_desc), flag(_flag), option(emp::to_string("--",_name)) - , args_label(_args_label) { } - virtual ~SettingBase() { } - - virtual size_t GetSize() const = 0; ///< How many values are available? - virtual std::string AsString() const = 0; ///< All values, as a single string. - virtual std::string AsString(size_t) const = 0; ///< A specified value as a string. - virtual bool FromString(const std::string_view &) = 0; ///< Convert string to set of settings. - virtual bool SetValueID(size_t) {return false; } ///< Setup cur value in linked variable - virtual bool IsComboSetting() { return false; } ///< Do we have a combo setting? - virtual size_t GetID() const { return (size_t) -1; } ///< Combination ID for this setting. - - bool IsOptionMatch(const std::string & test_option) const { return test_option == option; } - bool IsFlagMatch(const char test_flag) const { return test_flag == flag; } + std::string name; ///< Name for this setting + std::string desc; ///< Description of setting + char flag; ///< Command-line flag ('\0' for none) + std::string option; ///< Command-line longer option. + std::string args_label; ///< Label for option arguments (used in --help) + + SettingBase(const std::string &_name, const std::string &_desc, + const char _flag, const std::string &_args_label) + : name(_name), desc(_desc), flag(_flag), option(emp::to_string("--", _name)), args_label(_args_label) {} + virtual ~SettingBase() {} + + virtual emp::Ptr Clone() const = 0; + + virtual size_t GetSize() const = 0; ///< How many values are available? + virtual std::string AsString() const = 0; ///< All values, as a single string. + virtual std::string AsString(size_t) const = 0; ///< A specified value as a string. + virtual bool FromString(const std::string_view &) = 0; ///< Convert string to set of settings. + virtual bool SetValueID(size_t) { return false; } ///< Setup cur value in linked variable + virtual bool IsComboSetting() { return false; } ///< Do we have a combo setting? + virtual size_t GetID() const { return (size_t)-1; } ///< Combination ID for this setting. + + bool IsOptionMatch(const std::string &test_option) const { return test_option == option; } + bool IsFlagMatch(const char test_flag) const { return test_flag == flag; } }; /// Full details about a single setting, including type information and values. template struct SettingInfo : public SettingBase { - T value; - emp::Ptr var_ptr = nullptr; - - SettingInfo(const std::string & _name, ///< Unique name for this setting. - const std::string & _desc, ///< Description of this setting (for help) - const char _flag, ///< Single char flag for easy access (e.g., "-h") - const std::string & _arg, ///< Label for option argument (for help) - emp::Ptr _var=nullptr) ///< Pointer to variable to set (optional) - : SettingBase(_name, _desc, _flag, _arg), var_ptr(_var) { } - - size_t GetSize() const override { return 1; } - std::string AsString() const override { return emp::to_string(value); } - std::string AsString(size_t id) const override { - emp_assert(id == 0); - return emp::to_string(value); - } + T value; + emp::Ptr var_ptr = nullptr; + + SettingInfo(const std::string &_name, ///< Unique name for this setting. + const std::string &_desc, ///< Description of this setting (for help) + const char _flag, ///< Single char flag for easy access (e.g., "-h") + const std::string &_arg, ///< Label for option argument (for help) + emp::Ptr _var = nullptr) ///< Pointer to variable to set (optional) + : SettingBase(_name, _desc, _flag, _arg), var_ptr(_var) { ; } + + emp::Ptr Clone() const override { + auto si_ptr = emp::NewPtr>(name, desc, flag, args_label); + si_ptr->value = this->value; + return si_ptr; + } - bool FromString(const std::string_view & input) override { - value = emp::from_string(input); - // @CAO: Could do more tests to make sure whole string was used. - if (!var_ptr.IsNull()) *var_ptr = value; - return true; - } + size_t GetSize() const override { return 1; } + std::string AsString() const override { return emp::to_string(value); } + std::string AsString(size_t id) const override { + emp_assert(id == 0); + return emp::to_string(value); + } + + bool FromString(const std::string_view &input) override { + value = emp::from_string(input); + // @CAO: Could do more tests to make sure whole string was used. + if (!var_ptr.IsNull()) *var_ptr = value; + return true; + } }; /// Allow a single setting to have multiple values specified that should be stepped through. template struct ComboSettingInfo : public SettingBase { - emp::vector values; ///< Set of values to use for this setting. - emp::Ptr var_ptr = nullptr; ///< Pointer to variable to set as combos change. - size_t id; ///< Unique ID/position for this setting. - - ComboSettingInfo(const std::string & _name, ///< Unique name for this setting. - const std::string & _desc, ///< Description of this setting (for help) - const char _flag, ///< Char flag for easy access (e.g., "-h") - const std::string & _args, ///< Label for option arguments (for help) - emp::Ptr _var=nullptr) ///< Pointer to variable to set (optional) - : SettingBase(_name, _desc, _flag, _args), var_ptr(_var) { } - - size_t GetSize() const override { return values.size(); } - std::string AsString() const override { - std::stringstream ss; - for (size_t i=0; i < values.size(); i++) { - if (i) ss << ','; - ss << values[i]; + emp::vector values; ///< Set of values to use for this setting. + emp::Ptr var_ptr = nullptr; ///< Pointer to variable to set as combos change. + size_t id; ///< Unique ID/position for this setting. + + ComboSettingInfo(const std::string &_name, ///< Unique name for this setting. + const std::string &_desc, ///< Description of this setting (for help) + const char _flag, ///< Char flag for easy access (e.g., "-h") + const std::string &_args, ///< Label for option arguments (for help) + emp::Ptr _var = nullptr) ///< Pointer to variable to set (optional) + : SettingBase(_name, _desc, _flag, _args), var_ptr(_var) { ; } + + emp::Ptr Clone() const override { + auto csi_ptr = emp::NewPtr>(name, desc, flag, args_label); + csi_ptr->values = this->values; + return csi_ptr; } - return ss.str(); - } - std::string AsString(size_t id) const override { - return emp::to_string(values[id]); - } - bool FromString(const std::string_view & input) override { - // Divide up inputs into comma-separated units. - auto slices = emp::slice(input, ','); - - // Clear out the values to set, one at a time (in most cases, aleady clear) - values.resize(0); - - // Process each slice into one or more values. - for (auto & cur_str : slices) { - // If we are working with an arithmetic type, check if this is a range. - if constexpr (std::is_arithmetic::value) { - auto r_slices = emp::slice(cur_str, ':'); - if (r_slices.size() > 3) return false; // Error! Too many slices!!! - T start = emp::from_string( r_slices[0] ); - T end = emp::from_string( r_slices.back() ); // Same as start if one value. - T step = (r_slices.size() == 3) ? emp::from_string( r_slices[1] ) : 1; - while (start <= end) { - values.push_back(start); - start += step; + size_t GetSize() const override { return values.size(); } + std::string AsString() const override { + std::stringstream ss; + for (size_t i = 0; i < values.size(); i++) { + if (i) ss << ','; + ss << values[i]; } } @@ -140,21 +127,52 @@ namespace emp { } } - if (!var_ptr.IsNull() && values.size()) *var_ptr = values[0]; - return values.size(); // Must result in at least one value. - } + bool FromString(const std::string_view &input) override { + // Divide up inputs into comma-separated units. + auto slices = emp::slice(input, ','); + + // Clear out the values to set, one at a time (in most cases, aleady clear) + values.resize(0); + + // Process each slice into one or more values. + for (auto &cur_str : slices) { + // If we are working with an arithmetic type, check if this is a range. + if constexpr (std::is_arithmetic::value) { + auto r_slices = emp::slice(cur_str, ':'); + if (r_slices.size() > 3) return false; // Error! Too many slices!!! + T start = emp::from_string(r_slices[0]); + T end = emp::from_string(r_slices.back()); // Same as start if one value. + T step = (r_slices.size() == 3) ? emp::from_string(r_slices[1]) : 1; + while (start <= end) { + values.push_back(start); + start += step; + } + } + + // Otherwise do a direct conversion. + else { + values.push_back(emp::from_string(cur_str)); + } + } + + if (!var_ptr.IsNull() && values.size()) *var_ptr = values[0]; + return values.size(); // Must result in at least one value. + } - bool SetValueID(size_t id) override { if (var_ptr) *var_ptr = values[id]; return true; } - bool IsComboSetting() override { return true; } - size_t GetID() const override { return id; } + bool SetValueID(size_t id) override { + if (var_ptr) *var_ptr = values[id]; + return true; + } + bool IsComboSetting() override { return true; } + size_t GetID() const override { return id; } }; /// A setting that is just a flag with an action function to run if it's called. struct ActionFlag { - std::string name; ///< Name for this flag - std::string desc; ///< Description of flag - char flag; ///< Command-line flag ('\0' for none) - std::function fun; ///< Function to be called if flag is set. + std::string name; ///< Name for this flag + std::string desc; ///< Description of flag + char flag; ///< Command-line flag ('\0' for none) + std::function fun; ///< Function to be called if flag is set. }; std::string exe_name = ""; @@ -162,59 +180,94 @@ namespace emp { std::map> setting_map; ///< All settings by name std::map action_map; ///< Action flags - emp::vector> combo_settings; ///< Multi-value settings (in order) - emp::vector cur_combo; ///< Which combo settings are we currently using? - size_t combo_id = 0; ///< Unique value indicating which combination we are on. + emp::vector> combo_settings; ///< Multi-value settings (in order) + emp::vector cur_combo; ///< Which combo settings are we currently using? + size_t combo_id = 0; ///< Unique value indicating which combination we are on. emp::vector unused_args; // Arguments that we were unable to process. std::string errors; - public: + public: SettingConfig() = default; + SettingConfig(const SettingConfig &other) { + combo_settings.resize(other.combo_settings.size()); + for (size_t i = 0; i < combo_settings.size(); i++) { + combo_settings[i] = other.combo_settings[i]->Clone(); + } + for (const auto &entry : other.setting_map) { + setting_map[entry.first] = other.setting_map.at(entry.first)->Clone(); + } + + action_map = other.action_map; + cur_combo = other.cur_combo; + combo_id = other.combo_id; + unused_args = other.unused_args; + errors = other.errors; + exe_name = other.exe_name; + } + ~SettingConfig() { - for (auto [name,ptr] : setting_map) ptr.Delete(); + for (auto [name, ptr] : setting_map) ptr.Delete(); } - const std::string & GetExeName() const { return exe_name; } + const std::string &GetExeName() const { return exe_name; } size_t GetComboID() const { return combo_id; } - const emp::vector & GetUnusedArgs() const { return unused_args; } - const std::string & GetErrors() const { return errors; } + const emp::vector &GetUnusedArgs() const { return unused_args; } + const std::string &GetErrors() const { return errors; } bool HasUnusedArgs() const { return unused_args.size(); } bool HasErrors() const { return errors.size(); } + /// Retrieves all of the setting names in config and places into std::vector + std::vector GetSettingMapNames() const { + std::vector result; + for (const auto &p : setting_map) { + result.push_back(p.first); + } + return result; + } + + /// Retrieves all of the setting names in config and places into std::vector + std::vector> GetSettingMapBase() const { + std::vector> result; + for (const auto &p : setting_map) { + result.push_back(p.second); + } + return result; + } + /// Get the current value of a specified setting. template - const T & GetValue(const std::string & name) const { - emp_assert(emp::Has(setting_map, name), name); - emp::Ptr base_ptr = setting_map.find(name)->second; - - // If we have a combo setting, determine the current value. - if (base_ptr->IsComboSetting()) { - emp::Ptr> ptr = base_ptr.Cast>(); - size_t id = cur_combo[ptr->id]; - return ptr->values[id]; - } + const T &GetValue(const std::string &name) const { + emp_assert(emp::Has(setting_map, name), name); + emp::Ptr base_ptr = setting_map.find(name)->second; + + // If we have a combo setting, determine the current value. + if (base_ptr->IsComboSetting()) { + emp::Ptr> ptr = base_ptr.Cast>(); + size_t id = cur_combo[ptr->id]; + return ptr->values[id]; + } - // Otherwise we have a regular setting. - return base_ptr.Cast>()->value; + // Otherwise we have a regular setting. + return base_ptr.Cast>()->value; } /// Scan through all values and return the maximum. template - T MaxValue(const std::string & name) const { - emp_assert(emp::Has(setting_map, name), name); - emp::Ptr base_ptr = setting_map.find(name)->second; - - // If we have a combo setting, determine the current value. - if (base_ptr->IsComboSetting()) { - emp::Ptr> ptr = base_ptr.Cast>(); - return emp::FindMax(ptr->values); - } + T MaxValue(const std::string &name) const { + emp_assert(emp::Has(setting_map, name), name); + emp::Ptr base_ptr = setting_map.find(name)->second; + + // If we have a combo setting, determine the current value. + if (base_ptr->IsComboSetting()) { + emp::Ptr> ptr = base_ptr.Cast>(); + return emp::FindMax(ptr->values); + } - // Otherwise we have a regular setting with just one value. - return base_ptr.Cast>()->value; + // Otherwise we have a regular setting with just one value. + return base_ptr.Cast>()->value; } /// Add a new setting of a specified type. Returns the (initially empty) vector of values @@ -223,16 +276,27 @@ namespace emp { /// config.AddSetting("num_runs") = 200; template - T & AddSetting(const std::string & name, - const std::string & desc, - const char option_flag, - T & var, - const std::string & args_label="Value") - { - emp_assert(!emp::Has(setting_map, name)); - auto new_ptr = emp::NewPtr>(name, desc, option_flag, args_label, &var); - setting_map[name] = new_ptr; - return new_ptr->value; + T &AddSetting(const std::string &name, + const std::string &desc, + const char option_flag, + T &var, + const std::string &args_label = "Value") { + emp_assert(!emp::Has(setting_map, name)); + auto new_ptr = emp::NewPtr>(name, desc, option_flag, args_label, &var); + setting_map[name] = new_ptr; + return new_ptr->value; + } + + /// Add a new setting not linked to a variable + + template + T &AddSetting(const std::string &name, + const std::string &desc = "", + const char option_flag = '\0') { + emp_assert(!emp::Has(setting_map, name)); + auto new_ptr = emp::NewPtr>(name, desc, option_flag, "Value"); + setting_map[name] = new_ptr; + return new_ptr->value; } /// Add a new setting of a specified type. Returns the (initially empty) vector of values @@ -241,186 +305,184 @@ namespace emp { /// config.AddComboSetting("pop_size") = { 100,200,400,800 }; template - emp::vector & AddComboSetting(const std::string & name, - const std::string & desc="", - const char option_flag='\0') { - emp_assert(!emp::Has(setting_map, name)); - auto new_ptr = emp::NewPtr>(name, desc, option_flag, "Values..."); - new_ptr->id = combo_settings.size(); - combo_settings.push_back(new_ptr); - setting_map[name] = new_ptr; - cur_combo.push_back(0); - return new_ptr->values; + emp::vector &AddComboSetting(const std::string &name, + const std::string &desc = "", + const char option_flag = '\0') { + emp_assert(!emp::Has(setting_map, name)); + auto new_ptr = emp::NewPtr>(name, desc, option_flag, "Values..."); + new_ptr->id = combo_settings.size(); + combo_settings.push_back(new_ptr); + setting_map[name] = new_ptr; + cur_combo.push_back(0); + return new_ptr->values; } /// A setting can also be linked to a value that is kept up-to-date. template - emp::vector & AddComboSetting(const std::string & name, - const std::string & desc, - const char option_flag, - T & var, - const std::string & args_label="Values...") - { - emp_assert(!emp::Has(setting_map, name)); - auto new_ptr = emp::NewPtr>(name, desc, option_flag, args_label, &var); - new_ptr->id = combo_settings.size(); - combo_settings.push_back(new_ptr); - setting_map[name] = new_ptr; - cur_combo.push_back(0); - return new_ptr->values; + emp::vector &AddComboSetting(const std::string &name, + const std::string &desc, + const char option_flag, + T &var, + const std::string &args_label = "Values...") { + emp_assert(!emp::Has(setting_map, name)); + auto new_ptr = emp::NewPtr>(name, desc, option_flag, args_label, &var); + new_ptr->id = combo_settings.size(); + combo_settings.push_back(new_ptr); + setting_map[name] = new_ptr; + cur_combo.push_back(0); + return new_ptr->values; } - void AddAction(const std::string & name, - const std::string & desc, + void AddAction(const std::string &name, + const std::string &desc, const char flag, - std::function fun) - { - std::string name_option = emp::to_string("--", name); - std::string flag_option = emp::to_string("-", flag); - emp_assert(!emp::Has(action_map, name_option)); - emp_assert(!emp::Has(action_map, flag_option)); - action_map[name_option] = ActionFlag{ name, desc, flag, fun }; - action_map[flag_option] = ActionFlag{ name, desc, flag, fun }; + std::function fun) { + std::string name_option = emp::to_string("--", name); + std::string flag_option = emp::to_string("-", flag); + emp_assert(!emp::Has(action_map, name_option)); + emp_assert(!emp::Has(action_map, flag_option)); + action_map[name_option] = ActionFlag{name, desc, flag, fun}; + action_map[flag_option] = ActionFlag{name, desc, flag, fun}; } /// Access ALL values for a specified setting, to be modified freely. template - emp::vector & ComboValues(const std::string & name) { - emp_assert(emp::Has(setting_map, name), name); - emp_assert(setting_map[name]->IsComboSetting()); - return setting_map[name].DynamicCast>()->values; + emp::vector &ComboValues(const std::string &name) { + emp_assert(emp::Has(setting_map, name), name); + emp_assert(setting_map[name]->IsComboSetting()); + return setting_map[name].DynamicCast>()->values; } /// Start over stepping through all combinations of parameter values. void ResetCombos() { - // Setup as base combo. - for (size_t & x : cur_combo) x = 0; - combo_id = 0; + // Setup as base combo. + for (size_t &x : cur_combo) x = 0; + combo_id = 0; - // Setup all linked values. - for (auto x : combo_settings) x->SetValueID(0); + // Setup all linked values. + for (auto x : combo_settings) x->SetValueID(0); } /// Add a single new value to the specified setting. template - void AddComboValue(const std::string & name, T && val) { - emp_assert(emp::Has(setting_map, name), name); - emp_assert(setting_map[name]->IsComboSetting()); - auto ptr = setting_map[name].DynamicCast>(); - ptr->values.emplace_back(std::forward(val)); + void AddComboValue(const std::string &name, T &&val) { + emp_assert(emp::Has(setting_map, name), name); + emp_assert(setting_map[name]->IsComboSetting()); + auto ptr = setting_map[name].DynamicCast>(); + ptr->values.emplace_back(std::forward(val)); } /// Set all values for the specified setting. template - void SetComboValues(const std::string & name, T1 && val1, Ts &&... vals) { - emp_assert(emp::Has(setting_map, name)); - auto ptr = setting_map[name].DynamicCast>(); - emp::Append(ptr->values, std::forward(val1), std::forward(vals)...); + void SetComboValues(const std::string &name, T1 &&val1, Ts &&... vals) { + emp_assert(emp::Has(setting_map, name)); + auto ptr = setting_map[name].DynamicCast>(); + emp::Append(ptr->values, std::forward(val1), std::forward(vals)...); } /// Determine how many unique combinations there currently are. size_t CountCombos() { - size_t result = 1; - for (auto ptr : combo_settings) result *= ptr->GetSize(); - return result; + size_t result = 1; + for (auto ptr : combo_settings) result *= ptr->GetSize(); + return result; } /// Set the next combination of settings to be active. Return true if successful /// or false if we ran through all combinations and reset. bool NextCombo() { - combo_id++; - for (size_t i = 0; i < cur_combo.size(); i++) { - cur_combo[i]++; - - // Check if this new combo is valid. - if (cur_combo[i] < combo_settings[i]->GetSize()) { - combo_settings[i]->SetValueID( cur_combo[i] ); // Set value in linked variable. - return true; - } + combo_id++; + for (size_t i = 0; i < cur_combo.size(); i++) { + cur_combo[i]++; + + // Check if this new combo is valid. + if (cur_combo[i] < combo_settings[i]->GetSize()) { + combo_settings[i]->SetValueID(cur_combo[i]); // Set value in linked variable. + return true; + } // Since it's not, prepare to move on to the next one. cur_combo[i] = 0; combo_settings[i]->SetValueID(0); } - // No valid combo found. - combo_id = 0; - return false; + // No valid combo found. + combo_id = 0; + return false; } /// Get the set of headers used for the CSV file. - std::string GetSettingHeaders(const std::string & separator=",") { - std::string out_str; - for (auto [name,ptr] : setting_map) { - if (out_str.size()) out_str += separator; - out_str += ptr->name; - } - return out_str; + std::string GetSettingHeaders(const std::string &separator = ",") { + std::string out_str; + for (auto [name, ptr] : setting_map) { + if (out_str.size()) out_str += separator; + out_str += ptr->name; + } + return out_str; } /// Convert all of the current values into a comma-separated string. - std::string CurSettings(const std::string & separator=",") const { - std::string out_str; - for (auto [name,ptr] : setting_map) { - if (ptr) { - if (out_str.size()) out_str += separator; - out_str += ptr->IsComboSetting() ? ptr->AsString(cur_combo[ptr->GetID()]) : ptr->AsString(); + std::string CurSettings(const std::string &separator = ",") const { + std::string out_str; + for (auto [name, ptr] : setting_map) { + if (ptr) { + if (out_str.size()) out_str += separator; + out_str += ptr->IsComboSetting() ? ptr->AsString(cur_combo[ptr->GetID()]) : ptr->AsString(); + } } - } - return out_str; + return out_str; } /// Get the set of headers used for the CSV file. - std::string GetComboHeaders(const std::string & separator=",") { - std::string out_string; - for (size_t i = 0; i < combo_settings.size(); i++) { - if (i) out_string += separator; - out_string += combo_settings[i]->name; - } - return out_string; + std::string GetComboHeaders(const std::string &separator = ",") { + std::string out_string; + for (size_t i = 0; i < combo_settings.size(); i++) { + if (i) out_string += separator; + out_string += combo_settings[i]->name; + } + return out_string; } /// Convert all of the current values into a comma-separated string. - std::string CurComboString(const std::string & separator=",", - bool use_labels=false, ///< Print name with each value? - bool multi_only=false ///< Only print values that can change? - ) const { - std::string out_str; - for (size_t i = 0; i < cur_combo.size(); i++) { - if (multi_only && combo_settings[i]->GetSize() == 1) continue; - if (out_str.size()) out_str += separator; - if (use_labels) out_str += emp::to_string(combo_settings[i]->name, '='); - out_str += combo_settings[i]->AsString(cur_combo[i]); - } - return out_str; + std::string CurComboString(const std::string &separator = ",", + bool use_labels = false, ///< Print name with each value? + bool multi_only = false ///< Only print values that can change? + ) const { + std::string out_str; + for (size_t i = 0; i < cur_combo.size(); i++) { + if (multi_only && combo_settings[i]->GetSize() == 1) continue; + if (out_str.size()) out_str += separator; + if (use_labels) out_str += emp::to_string(combo_settings[i]->name, '='); + out_str += combo_settings[i]->AsString(cur_combo[i]); + } + return out_str; } /// Scan through all settings for a match option and return ID. - emp::Ptr FindOptionMatch(const std::string & option_name) { - for (const auto & [name, ptr] : setting_map) { - if (ptr->IsOptionMatch(option_name)) return ptr; - } - return nullptr; + emp::Ptr FindOptionMatch(const std::string &option_name) { + for (const auto &[name, ptr] : setting_map) { + if (ptr->IsOptionMatch(option_name)) return ptr; + } + return nullptr; } /// Scan through all settings for a match option and return ID. - emp::Ptr FindFlagMatch(const char symbol) { - for (const auto & [name, ptr] : setting_map) { - if (ptr->IsFlagMatch(symbol)) return ptr; - } - return nullptr; + emp::Ptr FindFlagMatch(const char symbol) { + for (const auto &[name, ptr] : setting_map) { + if (ptr->IsFlagMatch(symbol)) return ptr; + } + return nullptr; } /// Take an input set of config options, process them, and track set of unprocessed ones. - bool ProcessOptions(const emp::vector & args) { - exe_name = args[0]; - - for (size_t i = 1; i < args.size(); i++) { - const std::string & cur_arg = args[i]; - if (cur_arg.size() < 2 || cur_arg[0] != '-') { - unused_args.push_back(cur_arg); - continue; // If isn't an option, continue. - } + bool ProcessOptions(const emp::vector &args) { + exe_name = args[0]; + + for (size_t i = 1; i < args.size(); i++) { + const std::string &cur_arg = args[i]; + if (cur_arg.size() < 2 || cur_arg[0] != '-') { + unused_args.push_back(cur_arg); + continue; // If isn't an option, continue. + } // See if this is a fully spelled-out option. auto setting_ptr = FindOptionMatch(cur_arg); @@ -452,50 +514,49 @@ namespace emp { } } - // Or see of this is a flag trigger. - else if (Has(action_map, cur_arg)) { - action_map[cur_arg].fun(); - } + // Or see of this is a flag trigger. + else if (Has(action_map, cur_arg)) { + action_map[cur_arg].fun(); + } - // Otherwise this argument will go unused; send it back. - else unused_args.push_back(cur_arg); - } + // Otherwise this argument will go unused; send it back. + else + unused_args.push_back(cur_arg); + } - return true; + return true; } - bool ProcessOptions(int argc, char* argv[]) { - return ProcessOptions(emp::cl::args_to_strings(argc, argv)); + bool ProcessOptions(int argc, char *argv[]) { + return ProcessOptions(emp::cl::args_to_strings(argc, argv)); } - template void PrintHelp(const Ts &... examples) const { + std::cout << "Format: " << exe_name << " [OPTIONS...]\n" + << "\nSetting Options:\n"; + for (auto [name, ptr] : setting_map) { + std::string spacing(emp::Max(1, 12 - (int)ptr->args_label.size()), ' '); + std::cout << " -" << ptr->flag << " [" << ptr->args_label << "]" << spacing << ": " + << ptr->desc << " (--" << name << ") [" + << ptr->AsString() << "]\n"; + } - std::cout << "Format: " << exe_name << " [OPTIONS...]\n" - << "\nSetting Options:\n"; - for (auto [name, ptr] : setting_map) { - std::string spacing(emp::Max(1, 12 - (int) ptr->args_label.size()), ' '); - std::cout << " -" << ptr->flag << " [" << ptr->args_label << "]" << spacing << ": " - << ptr->desc << " (--" << name << ") [" - << ptr->AsString() << "]\n"; - } - - std::cout << "\nAction Options:\n"; - for (auto [name, action] : action_map) { - if (name.size() == 2) continue; // Skip flag entries. - std::cout << " -" << action.flag << " : " - << action.desc << " (" << name << ")\n"; - } + std::cout << "\nAction Options:\n"; + for (auto [name, action] : action_map) { + if (name.size() == 2) continue; // Skip flag entries. + std::cout << " -" << action.flag << " : " + << action.desc << " (" << name << ")\n"; + } - if constexpr (sizeof...(examples) > 0) { - std::cout << "\nExample: " << emp::to_string(examples...) << std::endl; - } + if constexpr (sizeof...(examples) > 0) { + std::cout << "\nExample: " << emp::to_string(examples...) << std::endl; + } - std::cout.flush(); + std::cout.flush(); } - }; +}; // namespace emp -} +} // namespace emp #endif diff --git a/include/emp/prefab/QueueManager.hpp b/include/emp/prefab/QueueManager.hpp index 8aeffe1148..c2479ad65b 100644 --- a/include/emp/prefab/QueueManager.hpp +++ b/include/emp/prefab/QueueManager.hpp @@ -6,13 +6,14 @@ * @file queue-manager.h * @brief A tool for processing multiple simulation runs onto a figure and table, displaying real-time statistics * @note Status: + * @author Juan Chavez and Emily Dolson */ /// The goal of creating this tool is to alleviate the process of running multiple simulations of the same kind within /// a queue structure. A user is able to visually see the results of their simulations, and each run held within the queue /// runs subsequently after the other. Real-time statistics, that the user is able to display, are posted within a table as each run is carried out. /// Here is a link to a blogpost that describes this tool's inpsiration, purpose, and required instructions: -/// (Link to blogpost) +/// https://mmore500.com/waves/blog/queuemanager.html #pragma once @@ -34,15 +35,11 @@ namespace emp { /// Information of each element within queue. This info represents the information required for each run to be processed. struct RunInfo { SettingConfig runinfo_config; - size_t id; - size_t cur_epoch; - size_t num_coop; - std::string num_defect; RunInfo(SettingConfig _config, size_t _id) - : runinfo_config(_config), id(_id), cur_epoch(0), num_coop(0), num_defect("") { ; } + : runinfo_config(_config), id(_id), cur_epoch(0) { ; } }; /// Primary class that establishes queue for runs and processes them accordingly @@ -53,15 +50,13 @@ class QueueManager { emp::web::Div display_div; std::string table_id; // ordered names of dependant headers with associated column #'s - emp::vector> ordered_names; - std::unordered_map> dependant_headers; - // for SimplePDWorld + // emp::vector ordered_param_names; + emp::vector ordered_metric_names; + emp::vector> metric_funs; size_t epoch_ = 0; - size_t coop_ = 0; public: void SetEpoch(size_t epoch) { epoch_ = epoch; } - void SetNumCoop(size_t coop) { coop_ = coop; } /// Default constructor QueueManager() = default; @@ -124,9 +119,8 @@ class QueueManager { /* if adding more features after this point, keep in mind of where the col count will be */ - for (auto& i : ordered_names) { - i.second = column_count; - result_tab.GetCell(0, column_count++).SetHeader() << i.first; + for (size_t i = 0; i < ordered_metric_names.size(); i++) { + result_tab.GetCell(0, column_count + i).SetHeader() << ordered_metric_names[i]; } display_div << result_tab; @@ -145,7 +139,7 @@ class QueueManager { my_table.GetCell(line_id, ++col_count) << (*p).AsString(); } - for (int i = 0; i < dependant_headers.size(); i++) { + for (int i = 0; i < ordered_metric_names.size(); i++) { my_table.GetCell(line_id, ++col_count) << "Waiting..."; // world.GetE(); world.CountCoop(); (world.GetN() - world.CountCoop()); } @@ -154,16 +148,6 @@ class QueueManager { my_table.Redraw(); } - /// Run info in table is updated - void DivInfoTable(size_t id, size_t cur_epoch, size_t num_coop, std::string num_defect) { - emp::web::Table my_table = display_div.Find(table_id); - my_table.Freeze(); - my_table.GetCell(id + 1, 5).ClearChildren() << cur_epoch; - my_table.GetCell(id + 1, 6).ClearChildren() << num_coop; - my_table.GetCell(id + 1, 7).ClearChildren() << num_defect; - my_table.Activate(); - } - /// Calculations required for updating table void DivTableCalc() { size_t id = FrontRun().id; @@ -171,19 +155,21 @@ class QueueManager { RunInfo& current_run = FrontRun(); current_run.cur_epoch = current_epoch; - current_run.num_coop = coop_; + emp::web::Table my_table = display_div.Find(table_id); + my_table.Freeze(); + my_table.GetCell(id + 1, 5).ClearChildren() << current_epoch; + // user function configuration - for (auto i : dependant_headers) { - if (i.first == "Num Defect") { - current_run.num_defect = (dependant_headers.begin()->second)(); + for (int i = 0; i < metric_funs.size(); i++) { + my_table.GetCell(id + 1, 5 + i).ClearChildren() << metric_funs[i](); } - } + // } if (current_epoch >= current_run.runinfo_config.GetValue("E_value")) { // Are we done with this run? RemoveRun(); // Updates to the next run } - DivInfoTable(id, current_epoch, current_run.num_coop, current_run.num_defect); + my_table.Activate(); } /// Creates area for user to input how many runs will be queued @@ -191,8 +177,7 @@ class QueueManager { size_t num_runs = 10; emp::web::TextArea run_input([&num_runs](const std::string& str) { num_runs = emp::from_string(str); - }, - "run_count"); + }, "run_count"); run_input.SetText(emp::to_string(num_runs)); display_div << run_input; @@ -206,15 +191,15 @@ class QueueManager { AddRun(queue_config); DivButtonTable(run_id); } - }, - "Queue", "queue_but"); + }, "Queue", "queue_but"); display_div << my_button; } /// Adds dependant variables to tables void AddDepVariable(std::function func, std::string header_name) { - ordered_names.push_back({header_name, 0}); - dependant_headers.insert({header_name, func}); + // ordered_param_names.push_back(header_name); + ordered_metric_names.insert(header_name); + metric_funs.insert(func); } }; From 058ce80b3fbdb23d974de700054279b161ff573b Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sun, 28 Feb 2021 04:02:03 -0500 Subject: [PATCH 03/19] Updated Spatial Coop demo to use QueueManager --- demos/SpatialCoop2017/Makefile | 4 +- .../SpatialCoop2017/source/SimplePDWorld.hpp | 13 +- .../source/web/SimplePDWorld-web.cpp | 324 +++++++----------- include/emp/config/SettingConfig.hpp | 6 - include/emp/prefab/QueueManager.hpp | 53 +-- 5 files changed, 168 insertions(+), 232 deletions(-) diff --git a/demos/SpatialCoop2017/Makefile b/demos/SpatialCoop2017/Makefile index dced09e09b..4dcf2e10e5 100644 --- a/demos/SpatialCoop2017/Makefile +++ b/demos/SpatialCoop2017/Makefile @@ -12,9 +12,9 @@ CFLAGS_nat_debug := -g $(CFLAGS_all) # Emscripten compiler information CXX_web := emcc -OFLAGS_web_all := -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap']" -s TOTAL_MEMORY=67108864 --js-library $(EMP_DIR)/emp/web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback']" -s DISABLE_EXCEPTION_CATCHING=1 -s NO_EXIT_RUNTIME=1 #--embed-file configs +OFLAGS_web_all := -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap']" -s TOTAL_MEMORY=67108864 --js-library $(EMP_DIR)/emp/web/library_emp.js -s EXPORTED_FUNCTIONS="['_main', '_empCppCallback', '_empDoCppCallback']" -s DISABLE_EXCEPTION_CATCHING=1 -s NO_EXIT_RUNTIME=1 -s WASM=0 #--embed-file configs OFLAGS_web := -Oz -DNDEBUG -OFLAGS_web_debug := -g4 -pedantic -Wno-dollar-in-identifier-extension +OFLAGS_web_debug := -pedantic -Wno-dollar-in-identifier-extension CFLAGS_web := $(CFLAGS_all) $(OFLAGS_web) $(OFLAGS_web_all) CFLAGS_web_debug := $(CFLAGS_all) $(OFLAGS_web_debug) $(OFLAGS_web_all) diff --git a/demos/SpatialCoop2017/source/SimplePDWorld.hpp b/demos/SpatialCoop2017/source/SimplePDWorld.hpp index 1d507500d1..ff87a60e7c 100644 --- a/demos/SpatialCoop2017/source/SimplePDWorld.hpp +++ b/demos/SpatialCoop2017/source/SimplePDWorld.hpp @@ -111,16 +111,17 @@ class SimplePDWorld { void Reset() { Setup(r,u,N,E); } - void Run(size_t steps=-1) { - if (steps > E) steps = E; - // Run the organisms! - size_t end_epoch = epoch + steps; - while (epoch < end_epoch) { + void Run(size_t steps=1) { + + for (size_t i = 0; i < steps; i++) { for (size_t o = 0; o < N; o++) Repro(); - epoch++; } } + void RunStep() { + for (size_t o = 0; o < N; o++) Repro(); + } + size_t CountCoop(); void PrintNeighborInfo(std::ostream & os); }; diff --git a/demos/SpatialCoop2017/source/web/SimplePDWorld-web.cpp b/demos/SpatialCoop2017/source/web/SimplePDWorld-web.cpp index 49bb79ce5b..d305ca0980 100644 --- a/demos/SpatialCoop2017/source/web/SimplePDWorld-web.cpp +++ b/demos/SpatialCoop2017/source/web/SimplePDWorld-web.cpp @@ -1,9 +1,14 @@ // This file is part of Project Name // Copyright (C) Michigan State University, 2017. // Released under the MIT Software license; see doc/LICENSE +// PD WORLD EXAMPLE -#include "emp/web/web.hpp" +#include +#include + +#include "emp/prefab/QueueManager.hpp" #include "../SimplePDWorld.hpp" +#include "emp/web/web.hpp" namespace UI = emp::web; @@ -15,210 +20,145 @@ SimplePDWorld world; int cur_x = -1; int cur_y = -1; +std::function SetupConfig = [](){ + emp::SettingConfig config; + config.AddSetting("r") = {world.GetR()}; + config.AddSetting("u") = {world.GetU()}; + config.AddSetting("N") = {world.GetN()}; + config.AddSetting("E") = {world.GetE()}; + + return config; +}; + +emp::SettingConfig config = SetupConfig(); +emp::QueueManager run_list(config); + void DrawCanvas() { - UI::Canvas canvas = doc.Canvas("canvas"); - canvas.Clear("black"); + UI::Canvas canvas = doc.Canvas("canvas"); + canvas.Clear("black"); - const emp::vector & pop = world.GetPop(); + const emp::vector& pop = world.GetPop(); - if (cur_x >= 0) { - canvas.Circle(cur_x, cur_y, world_size*world.GetR(), "pink"); - } + if (cur_x >= 0) { + canvas.Circle(cur_x, cur_y, world_size * world.GetR(), "pink"); + } - for (const Org & org : pop) { - if (org.coop) { - canvas.Circle(org.x*world_size, org.y*world_size, 2, "blue", "#8888FF"); - } else { - canvas.Circle(org.x*world_size, org.y*world_size, 2, "#FF8888", "red"); + for (const Org& org : pop) { + if (org.coop) { + canvas.Circle(org.x * world_size, org.y * world_size, 2, "blue", "#8888FF"); + } else { + canvas.Circle(org.x * world_size, org.y * world_size, 2, "#FF8888", "red"); + } } - } - doc.Text("ud_text").Redraw(); + doc.Text("ud_text").Redraw(); } void CanvasClick(int x, int y) { - cur_x = x; - cur_y = y; - DrawCanvas(); + cur_x = x; + cur_y = y; + DrawCanvas(); } -struct RunInfo { - size_t id; - - double r; - double u; - size_t N; - size_t E; - - size_t cur_epoch; - size_t num_coop; - size_t num_defect; - - RunInfo(size_t _id, double _r, double _u, size_t _N, size_t _E) - : id(_id), r(_r), u(_u), N(_N), E(_E) - , cur_epoch(0), num_coop(0), num_defect(0) - { ; } -}; - -struct RunList { - emp::vector runs; - size_t cur_run = 0; - - void AddRun(double r, double u, size_t N, size_t E) { - size_t id = runs.size(); - runs.emplace_back(id, r, u, N, E); - } - - bool Active() const { return cur_run < runs.size(); } -}; +void TogglePlay() { + auto& anim = doc.Animate("anim_world"); + anim.ToggleActive(); + auto but = doc.Button("start_but"); + if (anim.GetActive()) + but.SetLabel("Pause"); + else + but.SetLabel("Start"); + + but = doc.Button("run_but"); + if (anim.GetActive()) + but.SetLabel("Stop"); + else + but.SetLabel("Fast Forward!"); +} -RunList run_list; int anim_step = 1; -void TogglePlay() -{ - auto & anim = doc.Animate("anim_world"); - anim.ToggleActive(); - auto but = doc.Button("start_but"); - if (anim.GetActive()) but.SetLabel("Pause"); - else but.SetLabel("Start"); - - but = doc.Button("run_but"); - if (anim.GetActive()) but.SetLabel("Stop"); - else but.SetLabel("Fast Forward!"); -} - -int main() -{ - doc << "

Spatial Prisoner's Dilema

"; - auto canvas = doc.AddCanvas(world_size, world_size, "canvas"); - // canvas.On("click", CanvasClick); - auto & anim = doc.AddAnimation("anim_world", [](){ - if (run_list.Active()) { - size_t id = run_list.cur_run; - auto & run = run_list.runs[id]; - if (run.cur_epoch == 0) { // Are we starting a new run? - world.Setup(run.r, run.u, run.N, run.E); +int main() { + + std::function coop_func = []() { return std::to_string(world.CountCoop()); }; + std::function defect_func = []() { return std::to_string(run_list.FrontRun().runinfo_config.GetValue("N") - world.CountCoop()); }; + + run_list.AddDepVariable(coop_func, "Num Coop"); + run_list.AddDepVariable(defect_func, "Num Defect"); + doc << "

Spatial Prisoner's Dillemma

"; + auto canvas = doc.AddCanvas(world_size, world_size, "canvas"); + // canvas.On("click", CanvasClick); + auto& anim = doc.AddAnimation("anim_world", []() { + // if queue has runs + if (!run_list.IsEmpty()) { + emp::RunInfo & run = run_list.FrontRun(); // Referencing current run + if (run.cur_epoch == 0) { // Are we starting a new run? + world.Setup(run.runinfo_config.GetValue("r"), run.runinfo_config.GetValue("u"), run.runinfo_config.GetValue("N"), run.runinfo_config.GetValue("E")); + DrawCanvas(); + } + run.cur_epoch += anim_step; + } + world.Run(anim_step); DrawCanvas(); - } - } - world.Run(anim_step); - DrawCanvas(); - if (run_list.Active()) { - size_t id = run_list.cur_run; - size_t cur_epoch = world.GetEpoch(); - if (run_list.runs[id].E <= cur_epoch) { // Are we done with this run? - run_list.cur_run++; - } - run_list.runs[id].cur_epoch = cur_epoch; - run_list.runs[id].num_coop = world.CountCoop(); - run_list.runs[id].num_defect = run_list.runs[id].N - run_list.runs[id].num_coop; - - auto result_tab = doc.Table("result_tab"); - result_tab.Freeze(); - result_tab.GetCell(id+1,5).ClearChildren() << cur_epoch; - result_tab.GetCell(id+1,6).ClearChildren() << run_list.runs[id].num_coop; - result_tab.GetCell(id+1,7).ClearChildren() << run_list.runs[id].num_defect; - result_tab.Activate(); - } - } ); - - doc << "
"; - doc.AddButton([&anim](){ - anim_step = 1; - TogglePlay(); - }, "Play", "start_but"); - doc.AddButton([](){ world.Run(1); DrawCanvas(); }, "Step", "step_but"); - doc.AddButton([&anim](){ - anim_step = 100; - TogglePlay(); - }, "Fast Forward!", "run_but"); - doc.AddButton([](){ world.Reset(); DrawCanvas(); }, "Randomize", "rand_but"); - auto ud_text = doc.AddText("ud_text"); - ud_text << " Epoch = " << UI::Live(world.epoch); - - doc << "
Radius (r) = "; - doc.AddTextArea([](const std::string & str){ - double r = emp::from_string(str); - world.SetR(r); - }, "r_set").SetText(emp::to_string(world.GetR())); - - doc << "
cost/benefit ratio (u) = "; - doc.AddTextArea([](const std::string & str){ - double u = emp::from_string(str); - world.SetU(u); - }, "u_set").SetText(emp::to_string(world.GetU())); - - - doc << "
Population Size (N) = "; - doc.AddTextArea([](const std::string & str){ - size_t N = emp::from_string(str); - world.SetN(N); - }, "N_set").SetText(emp::to_string(world.GetN())); - - - doc << "
Num Epochs on Run (E) = "; - doc.AddTextArea([](const std::string & str){ - size_t E = emp::from_string(str); - world.SetE(E); - }, "E_set").SetText(emp::to_string(world.GetE())); - - doc << "
" - << "NOTE: You must hit 'Randomize' after changing any parameters for them to take effect." - << "
" - << "

Full Runs

" - << "You can perform many runs at once with the same configuration. " - << "Setup the configuration above, choose the number of runs, and queue them up (as many as you like, even with different parameters). " - << "The next time you start (or fast forward) above, it will start working its way through the queued runs. " - << "
" - << "How many runs? "; - - auto run_input = doc.AddTextArea([](const std::string & str){ - size_t num_runs = emp::from_string(str); - world.SetNumRuns(num_runs); - }, "run_count"); - run_input.SetText(emp::to_string(world.GetNumRuns())); - - doc.AddButton([run_input](){ - //size_t num_runs = emp::from_string(run_input.GetText()); - size_t num_runs = world.GetNumRuns(); - auto result_tab = doc.Table("result_tab"); - for (int run_id = 0; run_id < num_runs; run_id++) { - run_list.AddRun(world.GetR(), world.GetU(), world.GetN(), world.GetE()); - - // Update the table. - int line_id = result_tab.GetNumRows(); - result_tab.Rows(line_id+1); - result_tab.GetCell(line_id, 0) << run_id; - result_tab.GetCell(line_id, 1) << world.GetR(); - result_tab.GetCell(line_id, 2) << world.GetU(); - result_tab.GetCell(line_id, 3) << world.GetN(); - result_tab.GetCell(line_id, 4) << world.GetE(); - result_tab.GetCell(line_id, 5) << "Waiting..."; // world.GetE(); - result_tab.GetCell(line_id, 6) << "Waiting..."; // world.CountCoop(); - result_tab.GetCell(line_id, 7) << "Waiting..."; // (world.GetN() - world.CountCoop()); - - // Draw the new table. - result_tab.CellsCSS("border", "1px solid black"); - result_tab.Redraw(); - } - }, "Queue", "queue_but"); - - doc << "
"; - - auto result_tab = doc.AddTable(1,8, "result_tab"); - result_tab.SetCSS("border-collapse", "collapse"); - result_tab.SetCSS("border", "3px solid black"); - result_tab.CellsCSS("border", "1px solid black"); + if (!run_list.IsEmpty()) { + run_list.DivTableCalc(); //calculations for table + } + }); + + doc << "
"; + doc.AddButton([]() { + anim_step = 1; + TogglePlay(); + }, "Play", "start_but"); + doc.AddButton([]() { world.Run(1); DrawCanvas(); }, "Step", "step_but"); + doc.AddButton([]() { + anim_step = 100; + TogglePlay(); + }, "Fast Forward!", "run_but"); + doc.AddButton([]() { world.Reset(); DrawCanvas(); }, "Randomize", "rand_but"); + auto ud_text = doc.AddText("ud_text"); + ud_text << " Epoch = " << UI::Live(world.epoch); + + doc << "
Radius (r) = "; + doc.AddTextArea([](const std::string& str) { + double r = emp::from_string(str); + world.SetR(r); + }, "r_set").SetText(emp::to_string(world.GetR())); + + doc << "
cost/benefit ratio (u) = "; + doc.AddTextArea([](const std::string& str) { + double u = emp::from_string(str); + world.SetU(u); + }, "u_set").SetText(emp::to_string(world.GetU())); + + doc << "
Population Size (N) = "; + doc.AddTextArea([](const std::string& str) { + size_t N = emp::from_string(str); + world.SetN(N); + }, "N_set").SetText(emp::to_string(world.GetN())); + + doc << "
Num Epochs on Run (E) = "; + doc.AddTextArea([](const std::string& str) { + size_t E = emp::from_string(str); + world.SetE(E); + }, "E_set").SetText(emp::to_string(world.GetE())); + + doc << "
" + << "NOTE: You must hit 'Randomize' after changing any parameters for them to take effect." + << "
" + << "

Full Runs

" + << "You can perform many runs at once with the same configuration. " + << "Setup the configuration above, choose the number of runs, and queue them up (as many as you like, even with different parameters). " + << "The next time you start (or fast forward) above, it will start working its way through the queued runs. " + << "
" + << "How many runs? "; + + run_list.AddQueueButton([](){return SetupConfig();}, [](){return world.GetE();}); + + doc << "
"; + + doc << run_list.GetDiv(); + run_list.DivAddTable(1, 8, "result_tab"); - result_tab.GetCell(0,0).SetHeader() << "ID"; - result_tab.GetCell(0,1).SetHeader() << "r"; - result_tab.GetCell(0,2).SetHeader() << "u"; - result_tab.GetCell(0,3).SetHeader() << "N"; - result_tab.GetCell(0,4).SetHeader() << "E"; - result_tab.GetCell(0,5).SetHeader() << "Epoch"; - result_tab.GetCell(0,6).SetHeader() << "Num Coop"; - result_tab.GetCell(0,7).SetHeader() << "Num Defect"; - - DrawCanvas(); -} + DrawCanvas(); +} \ No newline at end of file diff --git a/include/emp/config/SettingConfig.hpp b/include/emp/config/SettingConfig.hpp index c23d24622a..97d68ff80d 100644 --- a/include/emp/config/SettingConfig.hpp +++ b/include/emp/config/SettingConfig.hpp @@ -119,12 +119,6 @@ class SettingConfig { if (i) ss << ','; ss << values[i]; } - } - - // Otherwise do a direct conversion. - else { - values.push_back( emp::from_string(cur_str) ); - } } bool FromString(const std::string_view &input) override { diff --git a/include/emp/prefab/QueueManager.hpp b/include/emp/prefab/QueueManager.hpp index c2479ad65b..9356ada526 100644 --- a/include/emp/prefab/QueueManager.hpp +++ b/include/emp/prefab/QueueManager.hpp @@ -37,6 +37,7 @@ struct RunInfo { SettingConfig runinfo_config; size_t id; size_t cur_epoch; + size_t epochs; RunInfo(SettingConfig _config, size_t _id) : runinfo_config(_config), id(_id), cur_epoch(0) { ; } @@ -48,12 +49,16 @@ class QueueManager { SettingConfig queue_config; std::queue runs; emp::web::Div display_div; + emp::web::TextArea run_input; std::string table_id; + // ordered names of dependant headers with associated column #'s // emp::vector ordered_param_names; emp::vector ordered_metric_names; emp::vector> metric_funs; + size_t epoch_ = 0; + size_t num_runs = 10; public: void SetEpoch(size_t epoch) { epoch_ = epoch; } @@ -75,8 +80,9 @@ class QueueManager { } /// Adds run to queue with run info for paramters - void AddRun(SettingConfig other) { + void AddRun(SettingConfig other, size_t _epochs) { RunInfo new_run(other, runs.size()); + new_run.epochs = _epochs; runs.push(new_run); } @@ -117,6 +123,8 @@ class QueueManager { ++column_count; } + result_tab.GetCell(0, column_count).SetHeader() << "Epoch"; + ++column_count; /* if adding more features after this point, keep in mind of where the col count will be */ for (size_t i = 0; i < ordered_metric_names.size(); i++) { @@ -135,12 +143,12 @@ class QueueManager { my_table.Rows(line_id + 1); int col_count = 0; my_table.GetCell(line_id, col_count) << run_id; - for (auto p : queue_config.GetSettingMapBase()) { + for (auto p : runs.back().runinfo_config.GetSettingMapBase()) { my_table.GetCell(line_id, ++col_count) << (*p).AsString(); } for (int i = 0; i < ordered_metric_names.size(); i++) { - my_table.GetCell(line_id, ++col_count) << "Waiting..."; // world.GetE(); world.CountCoop(); (world.GetN() - world.CountCoop()); + my_table.GetCell(line_id, ++col_count) << "Waiting..."; } // Draw the new table. @@ -151,44 +159,37 @@ class QueueManager { /// Calculations required for updating table void DivTableCalc() { size_t id = FrontRun().id; - size_t current_epoch = epoch_; RunInfo& current_run = FrontRun(); - current_run.cur_epoch = current_epoch; emp::web::Table my_table = display_div.Find(table_id); my_table.Freeze(); - my_table.GetCell(id + 1, 5).ClearChildren() << current_epoch; + my_table.GetCell(id + 1, 5).ClearChildren() << emp::to_string(current_run.cur_epoch); // user function configuration for (int i = 0; i < metric_funs.size(); i++) { - my_table.GetCell(id + 1, 5 + i).ClearChildren() << metric_funs[i](); - } - // } + my_table.GetCell(id + 1, 6 + i).ClearChildren() << metric_funs[i](); + } + - if (current_epoch >= current_run.runinfo_config.GetValue("E_value")) { // Are we done with this run? - RemoveRun(); // Updates to the next run + if (current_run.cur_epoch >= current_run.epochs) { // Are we done with this run? + RemoveRun(); // Updates to the next run } my_table.Activate(); } - /// Creates area for user to input how many runs will be queued - size_t DivAddTextArea() { - size_t num_runs = 10; - emp::web::TextArea run_input([&num_runs](const std::string& str) { - num_runs = emp::from_string(str); - }, "run_count"); + /// Creates queue button + void AddQueueButton(std::function get_conf, std::function get_epochs) { + run_input = emp::web::TextArea([this](const std::string & str){ + this->num_runs = emp::from_string(str); + }, "run_count"); run_input.SetText(emp::to_string(num_runs)); display_div << run_input; - return num_runs; - } - /// Creates queue button - void DivButton(size_t num_runs) { - emp::web::Button my_button([this, num_runs]() { - for (int run_id = 0; run_id < num_runs; run_id++) { - AddRun(queue_config); + emp::web::Button my_button([this, get_conf, get_epochs]() { + for (int run_id = 0; run_id < this->num_runs; run_id++) { + AddRun(get_conf(), get_epochs()); DivButtonTable(run_id); } }, "Queue", "queue_but"); @@ -198,8 +199,8 @@ class QueueManager { /// Adds dependant variables to tables void AddDepVariable(std::function func, std::string header_name) { // ordered_param_names.push_back(header_name); - ordered_metric_names.insert(header_name); - metric_funs.insert(func); + ordered_metric_names.push_back(header_name); + metric_funs.push_back(func); } }; From f19c10caf00aa9f43d50258dcff6833028d67224 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Sun, 28 Feb 2021 18:18:51 -0500 Subject: [PATCH 04/19] Cleanup and documentation --- .../source/web/SimplePDWorld-web.cpp | 16 +- include/emp/prefab/QueueManager.hpp | 281 +++++++++++++----- 2 files changed, 209 insertions(+), 88 deletions(-) diff --git a/demos/SpatialCoop2017/source/web/SimplePDWorld-web.cpp b/demos/SpatialCoop2017/source/web/SimplePDWorld-web.cpp index d305ca0980..a8b6f1b630 100644 --- a/demos/SpatialCoop2017/source/web/SimplePDWorld-web.cpp +++ b/demos/SpatialCoop2017/source/web/SimplePDWorld-web.cpp @@ -83,25 +83,25 @@ int main() { std::function coop_func = []() { return std::to_string(world.CountCoop()); }; std::function defect_func = []() { return std::to_string(run_list.FrontRun().runinfo_config.GetValue("N") - world.CountCoop()); }; - run_list.AddDepVariable(coop_func, "Num Coop"); - run_list.AddDepVariable(defect_func, "Num Defect"); + run_list.AddMetric(coop_func, "Num Coop"); + run_list.AddMetric(defect_func, "Num Defect"); doc << "

Spatial Prisoner's Dillemma

"; auto canvas = doc.AddCanvas(world_size, world_size, "canvas"); // canvas.On("click", CanvasClick); - auto& anim = doc.AddAnimation("anim_world", []() { + doc.AddAnimation("anim_world", []() { // if queue has runs if (!run_list.IsEmpty()) { - emp::RunInfo & run = run_list.FrontRun(); // Referencing current run - if (run.cur_epoch == 0) { // Are we starting a new run? + emp::QueueManager::RunInfo & run = run_list.FrontRun(); // Referencing current run + if (run.GetEpoch() == 0) { // Are we starting a new run? world.Setup(run.runinfo_config.GetValue("r"), run.runinfo_config.GetValue("u"), run.runinfo_config.GetValue("N"), run.runinfo_config.GetValue("E")); DrawCanvas(); } - run.cur_epoch += anim_step; + run.IncEpoch(anim_step); } world.Run(anim_step); DrawCanvas(); if (!run_list.IsEmpty()) { - run_list.DivTableCalc(); //calculations for table + run_list.Update(); //calculations for table } }); @@ -158,7 +158,7 @@ int main() { doc << "
"; doc << run_list.GetDiv(); - run_list.DivAddTable(1, 8, "result_tab"); + run_list.BuildTable(); DrawCanvas(); } \ No newline at end of file diff --git a/include/emp/prefab/QueueManager.hpp b/include/emp/prefab/QueueManager.hpp index 9356ada526..4e7d3843f3 100644 --- a/include/emp/prefab/QueueManager.hpp +++ b/include/emp/prefab/QueueManager.hpp @@ -1,20 +1,88 @@ /** * @note This file is part of Empirical, https://github.com/devosoft/Empirical * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md - * @date 2020 + * @date 2021 * * @file queue-manager.h - * @brief A tool for processing multiple simulation runs onto a figure and table, displaying real-time statistics + * @brief A web widget for managing queued runs with different settings and displaying real-time statistics * @note Status: * @author Juan Chavez and Emily Dolson + * + * This tool provides a web interface that allows users of the web version of a program to queue up multiple + * runs of the program with different settings. It can produce a table that displays the progress of these + * runs and user-defined statistics about them. + * + * QueueManager uses SettingConfig objects to keep track of parameter values for each run. It requires + * a SettingConfig on construction in order to initialize the table header correctly. When runs are queued, + * they will each require their own SettingConfig option. All SettingConfigs used in the same QueueManager + * must have the same parameters, although those parameters can have different values. + * + * Example of constructing a QueueManager: + * + * emp::SettingConfig my_settings; + * my_settings.AddSetting("my_param") = {.5}; + * emp::QueueManager my_queue_manager = emp::QueueManager(my_settings); + * + * Once a QueueManager has been constructed, it can be told to keep track of additional metrics about the + * world via the AddMetric() method. This method takes a function to calculate the metric and a name for + * the column in the table containing the metric as input. + * + * Example of adding metrics: + * + * my_queue_manager.AddMetric([](){return emp::to_string(std::chrono::system_clock::now());}, "Wall time"); + * + * Once a QueueManager has all the metrics set up, a button and text input for queueing runs and a table for + * displaying progress can be added to a web page. To add the button/text input for queueing, the QueueManager + * needs to know two things: where to get the SettingConfig for the newly-queued runs, and how to figure out + * how many epochs (time steps) to run them for. Both can be specified with functions. + * + * Example: + * + * int epochs = 0; + * std::function get_setting_config = [](){ + * emp::SettingConfig my_settings; + * my_settings.AddSetting("my_param") = {.9}; + * return my_settings; + * }; + * std::function get_epochs = [&epochs](){return epochs;}; + * + * emp::web::Document doc("emp_base"); + * my_queue_manager.AddQueueButton(get_setting_config, get_epochs); // Add button and text input + * my_queue_manager.BuildTable(); // Add progress table + * doc << my_queue_manager.GetDiv(); // Get the div storing QueueManager stuff and put it on the document + * + * Once your QueueManager is set up, you'll need integrate it with the rest of your code so that it + * appropriately pulls new runs from the queue when appropriate, does set up for each run at the beginning, + * and updates the current epoch and table when appropriate. The way to do this will vary based on the rest + * of your code. Assuming each run of your code involves a loop that occurs for a certain number of time + * steps (which correspond to epochs in the QueueManger), here is a template for how you might set up + * the body of that loop: + * + * if (!run_list.IsEmpty()) { // If there isn't stuff in the queue, support running program normally + * emp::QueueManager::RunInfo & run = run_list.FrontRun(); + * if (run.GetEpoch() == 0) { // Are we starting a new run? + * // Add beginning of run set up here + * // e.g. load in parameter settings from run.GetConfig() + * // If you want to do an initial visualization of something, + * // that goes here too. + * // Make sure to fully clean up from any previous run + * } + * run.IncEpoch(); // Assumes the loop this is inside runs once per time step + * } + * + * // Add the rest of the work for an individual time step of your code here + * // e.g. update the world, draw stuff, etc. + * + * if (!run_list.IsEmpty()) { // If we're running something from the queue, update the table + * run_list.Update(); // and check whether we have finished the current job + * } + * + * For an example of this template in action, see the SpatialCoop2017 demo. + * + * More information is available in this blog post: https://mmore500.com/waves/blog/queuemanager.html (this + * tool was written by Juan Chavez as part of WAVES 2020) */ -/// The goal of creating this tool is to alleviate the process of running multiple simulations of the same kind within -/// a queue structure. A user is able to visually see the results of their simulations, and each run held within the queue -/// runs subsequently after the other. Real-time statistics, that the user is able to display, are posted within a table as each run is carried out. -/// Here is a link to a blogpost that describes this tool's inpsiration, purpose, and required instructions: -/// https://mmore500.com/waves/blog/queuemanager.html - #pragma once #include @@ -32,142 +100,178 @@ namespace emp { -/// Information of each element within queue. This info represents the information required for each run to be processed. -struct RunInfo { - SettingConfig runinfo_config; - size_t id; - size_t cur_epoch; - size_t epochs; - - RunInfo(SettingConfig _config, size_t _id) - : runinfo_config(_config), id(_id), cur_epoch(0) { ; } -}; - /// Primary class that establishes queue for runs and processes them accordingly class QueueManager { + public: + + /// Information of each element within queue. This info represents the information required for each run to be processed. + struct RunInfo { + SettingConfig runinfo_config; // Holds all program-specific settings + size_t id; // The id of this run in the queue + size_t cur_epoch; // The current epoch that this run is on (will either be 0 or `epochs` unless this run is in progress) + size_t epochs; // The number of epochs this run is supposed to run for + + RunInfo(SettingConfig _config, size_t _id) + : runinfo_config(_config), id(_id), cur_epoch(0) { ; } + + /// @returns current epoch + size_t GetEpoch() {return cur_epoch;} + /// Increment current epoch by @param x + void IncEpoch(int x = 1) {cur_epoch += x;} + /// @returns configuration for this run + SettingConfig GetConfig() {return runinfo_config;} + }; + + private: SettingConfig queue_config; - std::queue runs; + std::queue runs; emp::web::Div display_div; emp::web::TextArea run_input; - std::string table_id; + emp::web::Button queue_button; + emp::web::Table display_table; - // ordered names of dependant headers with associated column #'s - // emp::vector ordered_param_names; emp::vector ordered_metric_names; emp::vector> metric_funs; - size_t epoch_ = 0; size_t num_runs = 10; + bool table_built = false; public: - void SetEpoch(size_t epoch) { epoch_ = epoch; } - /// Default constructor - QueueManager() = default; - - /// Config constructor + /// @param user_config An example configuration file for this program. Used to initialize table headers. QueueManager(SettingConfig user_config) : queue_config(user_config) { ; } - /// Checks if queue is empty + /// @returns True if queue is empty, false if it is not bool IsEmpty() { return runs.empty(); } - /// Checks how runs are in the queue + /// @returns Number of runs remaining in the queue size_t RunsRemaining() { return runs.size(); } - /// Adds run to queue with run info for paramters - void AddRun(SettingConfig other, size_t _epochs) { - RunInfo new_run(other, runs.size()); - new_run.epochs = _epochs; + /// Adds new run to queue using settings specified in @param settings. + /// @param epochs indicates how many epochs this run should run for. + void AddRun(SettingConfig settings, size_t epochs) { + RunInfo new_run(settings, runs.size()); + new_run.epochs = epochs; runs.push(new_run); } - /// Remove run from front of queue + /// Removes run from front of queue void RemoveRun() { emp_assert(!IsEmpty(), "Queue is empty! Cannot remove!"); runs.pop(); } - /// Front Run Getter + /// @returns The a reference to the first run in the queue + /// (i.e. the one that is running currently or, if none are + /// in progress, the next run) RunInfo& FrontRun() { emp_assert(!IsEmpty(), "Queue is empty! Cannot access Front!"); return runs.front(); } - /// Returns this dic + /// @returns the Div associated with this QueueManager. emp::web::Div GetDiv() { return display_div; } - /// Clears the content of this div + /// Clears the content of the div associated with this QueueManager void ResetDiv() { display_div.Clear(); + table_built = false; } - /// Initializes table to web - void DivAddTable(size_t row, size_t col, std::string id) { - table_id = id; - emp::web::Table result_tab(row, col, id); - result_tab.SetCSS("border-collapse", "collapse"); - result_tab.SetCSS("border", "3px solid black"); - result_tab.CellsCSS("border", "1px solid black"); + /// Adds table containing information for this QueueManager to the + /// Div associated with this QueueManager. + /// + /// @param id optionally allows you to choose the table's element id, + /// for ease of finding it from other parts of your code. + /// + /// Note that you still need to add this div to your document, + /// e.g. `my_doc << my_queue_manager.GetDiv()`; + void BuildTable(const std::string & id = "") { + emp_assert(!table_built && + "Trying to add QueueManager table but QueueManager table already built"); + + // Get parameter names + emp::vector setting_names = queue_config.GetSettingMapNames(); + + // Total number of columns is number of params + number of metrics + + // a column for the run id and a column for the current epoch. + size_t col = 2 + setting_names.size() + ordered_metric_names.size(); - result_tab.GetCell(0, 0).SetHeader() << "Run"; + // Make and style table + display_table = emp::web::Table(1, col, id); + display_table.SetCSS("border-collapse", "collapse"); + display_table.SetCSS("border", "3px solid black"); + display_table.CellsCSS("border", "1px solid black"); + + // Fill out header + display_table.GetCell(0, 0).SetHeader() << "Run"; int column_count = 1; - emp::vector setting_names = queue_config.GetSettingMapNames(); for (const auto& p : setting_names) { - result_tab.GetCell(0, column_count).SetHeader() << "" << p << ""; + display_table.GetCell(0, column_count).SetHeader() << "" << p << ""; ++column_count; } - result_tab.GetCell(0, column_count).SetHeader() << "Epoch"; + display_table.GetCell(0, column_count).SetHeader() << "Epoch"; ++column_count; - /* if adding more features after this point, keep in mind of where - the col count will be */ + + // if adding more features after this point, keep in mind of where + // the col count will be for (size_t i = 0; i < ordered_metric_names.size(); i++) { - result_tab.GetCell(0, column_count + i).SetHeader() << ordered_metric_names[i]; + display_table.GetCell(0, column_count + i).SetHeader() << ordered_metric_names[i]; } - display_div << result_tab; + display_div << display_table; + table_built = true; } - /// Extends table once button is clicked - void DivButtonTable(int run_id) { - emp::web::Table my_table = display_div.Find(table_id); + /// Helper function to add the last run in the queue to the table + /// Called by queue button. + void AddNewQueuedRunToTable() { + emp_assert(table_built && + "Trying to add run to QueueManager table but table hasn't been initialized. Call BuildTable first."); // Update the table. - int line_id = my_table.GetNumRows(); - my_table.Rows(line_id + 1); + int line_id = display_table.GetNumRows(); + display_table.Rows(line_id + 1); int col_count = 0; - my_table.GetCell(line_id, col_count) << run_id; + display_table.GetCell(line_id, col_count) << runs.back().id; + + // Add correct parameter values for (auto p : runs.back().runinfo_config.GetSettingMapBase()) { - my_table.GetCell(line_id, ++col_count) << (*p).AsString(); + display_table.GetCell(line_id, ++col_count) << (*p).AsString(); } - for (int i = 0; i < ordered_metric_names.size(); i++) { - my_table.GetCell(line_id, ++col_count) << "Waiting..."; + // Add placeholders for metrics and epoch column + for (int i = 0; i < ordered_metric_names.size() + 1; i++) { + display_table.GetCell(line_id, ++col_count) << "Waiting..."; } // Draw the new table. - my_table.CellsCSS("border", "1px solid black"); - my_table.Redraw(); + display_table.CellsCSS("border", "1px solid black"); + display_table.Redraw(); } - /// Calculations required for updating table - void DivTableCalc() { + /// Update QueueManager to reflect current status of runs and metrics. + /// Handles updating table and updating queue (checking if current run is done). + void Update() { + emp_assert(table_built && + "Trying to update QueueManager table but table hasn't been initialized. Call BuildTable first."); + size_t id = FrontRun().id; RunInfo& current_run = FrontRun(); - emp::web::Table my_table = display_div.Find(table_id); - my_table.Freeze(); - my_table.GetCell(id + 1, 5).ClearChildren() << emp::to_string(current_run.cur_epoch); + display_table.Freeze(); + display_table.GetCell(id + 1, 5).ClearChildren() << emp::to_string(current_run.cur_epoch); // user function configuration for (int i = 0; i < metric_funs.size(); i++) { - my_table.GetCell(id + 1, 6 + i).ClearChildren() << metric_funs[i](); + display_table.GetCell(id + 1, 6 + i).ClearChildren() << metric_funs[i](); } @@ -175,11 +279,18 @@ class QueueManager { RemoveRun(); // Updates to the next run } - my_table.Activate(); + display_table.Activate(); } - /// Creates queue button + /// Adds a button and text input to this QueueManager's div, allowing the user to queue runs. + /// The text input allows the user to enter a number specifying the number of runs to be queued. + /// Clicking the button queues the runs. + /// + /// @param get_conf is a function that creates and returns a SettingConfig object containing all of + /// the parameters that should be used for the run to be queued. + /// @param get_epochs is a function that will be used to determine how many epochs/time steps + /// the run is supposed to go for void AddQueueButton(std::function get_conf, std::function get_epochs) { run_input = emp::web::TextArea([this](const std::string & str){ this->num_runs = emp::from_string(str); @@ -187,20 +298,30 @@ class QueueManager { run_input.SetText(emp::to_string(num_runs)); display_div << run_input; - emp::web::Button my_button([this, get_conf, get_epochs]() { - for (int run_id = 0; run_id < this->num_runs; run_id++) { + queue_button = emp::web::Button([this, get_conf, get_epochs]() { + for (int i = 0; i < this->num_runs; i++) { AddRun(get_conf(), get_epochs()); - DivButtonTable(run_id); + AddNewQueuedRunToTable(); } }, "Queue", "queue_but"); - display_div << my_button; + display_div << queue_button; } - /// Adds dependant variables to tables - void AddDepVariable(std::function func, std::string header_name) { - // ordered_param_names.push_back(header_name); + /// Adds new metric to table + /// @param func the function to be called to calculate this metric. Must take + /// no arguments and return a string. If you need arguments, we reccomend using + /// a lambda function and capturing the information you need instead. + /// @param header_name the name this column should have in the table + /// + void AddMetric(std::function func, std::string header_name) { ordered_metric_names.push_back(header_name); metric_funs.push_back(func); + + if (table_built) { + size_t col_id = display_table.GetNumCols(); + display_table.Cols(col_id + 1); + display_table.GetCell(0, col_id).SetHeader() << header_name; + } } }; From e95018e190c3396c3a44a1d2be86c357bf01622e Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Mon, 1 Mar 2021 02:39:17 -0500 Subject: [PATCH 05/19] Add tests for QueueManager --- include/emp/prefab/QueueManager.hpp | 11 +- tests/web/Makefile | 2 +- tests/web/QueueManager.cpp | 163 ++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 tests/web/QueueManager.cpp diff --git a/include/emp/prefab/QueueManager.hpp b/include/emp/prefab/QueueManager.hpp index 4e7d3843f3..50a7997dd6 100644 --- a/include/emp/prefab/QueueManager.hpp +++ b/include/emp/prefab/QueueManager.hpp @@ -38,13 +38,12 @@ * * Example: * - * int epochs = 0; * std::function get_setting_config = [](){ * emp::SettingConfig my_settings; * my_settings.AddSetting("my_param") = {.9}; * return my_settings; * }; - * std::function get_epochs = [&epochs](){return epochs;}; + * std::function get_epochs = [](){return 50;}; // Always run for 50 epochs * * emp::web::Document doc("emp_base"); * my_queue_manager.AddQueueButton(get_setting_config, get_epochs); // Add button and text input @@ -116,6 +115,8 @@ class QueueManager { /// @returns current epoch size_t GetEpoch() {return cur_epoch;} + /// @returns number of epochs to run for + size_t GetNEpochs() {return epochs;} /// Increment current epoch by @param x void IncEpoch(int x = 1) {cur_epoch += x;} /// @returns configuration for this run @@ -266,12 +267,14 @@ class QueueManager { size_t id = FrontRun().id; RunInfo& current_run = FrontRun(); + size_t n_settings = queue_config.GetSettingMapNames().size(); + display_table.Freeze(); - display_table.GetCell(id + 1, 5).ClearChildren() << emp::to_string(current_run.cur_epoch); + display_table.GetCell(id + 1, n_settings+1).ClearChildren() << emp::to_string(current_run.cur_epoch); // user function configuration for (int i = 0; i < metric_funs.size(); i++) { - display_table.GetCell(id + 1, 6 + i).ClearChildren() << metric_funs[i](); + display_table.GetCell(id + 1, n_settings + 2 + i).ClearChildren() << metric_funs[i](); } diff --git a/tests/web/Makefile b/tests/web/Makefile index b797b85b9f..844e97ecd5 100644 --- a/tests/web/Makefile +++ b/tests/web/Makefile @@ -1,6 +1,6 @@ SHELL := /bin/bash -TEST_NAMES = ConfigPanel Collapse LoadingModal Card CommentBox Modal ToggleSwitch CodeBlock LoadingIcon FontAwesomeIcon ClickCounterDemo ClickCollapseDemo Element TextFeed js_utils visualizations JSWrap Widget +TEST_NAMES = ConfigPanel Collapse LoadingModal Card CommentBox Modal ToggleSwitch CodeBlock LoadingIcon FontAwesomeIcon ClickCounterDemo ClickCollapseDemo Element TextFeed js_utils visualizations JSWrap Widget QueueManager # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -Wno-gnu-zero-variadic-macro-arguments -Wno-dollar-in-identifier-extension -std=c++17 -I../../include/ -I../../ diff --git a/tests/web/QueueManager.cpp b/tests/web/QueueManager.cpp new file mode 100644 index 0000000000..ae3bea2844 --- /dev/null +++ b/tests/web/QueueManager.cpp @@ -0,0 +1,163 @@ +// This file is part of Empirical, https://github.com/devosoft/Empirical +// Copyright (C) Michigan State University, 2020. +// Released under the MIT Software license; see doc/LICENSE + +#include +#include + +#include "emp/base/assert.hpp" +#include "emp/web/_MochaTestRunner.hpp" +#include "emp/web/Document.hpp" +#include "emp/prefab/QueueManager.hpp" +#include "emp/config/SettingConfig.hpp" +#include "emp/web/web.hpp" + + +struct Test_QueueManager : public emp::web::BaseTest { + + std::function get_setting_config = [](){ + emp::SettingConfig my_settings; + my_settings.AddSetting("my_param") = {9}; + return my_settings; + }; + + std::function get_epochs = [](){return 50;}; + std::function metric_fun = [](){return "Hello";}; + + emp::SettingConfig config; + emp::Ptr queue_manager; + + Test_QueueManager() + : BaseTest({"emp_test_container"}) // we can tell BaseTest that we want to create a set of emp::web::Document + // objects for each given html element ids. + { + + config.AddSetting("my_param") = {6}; + queue_manager.New(config); + + queue_manager->AddMetric(metric_fun, "Hello metric"); + + queue_manager->AddQueueButton(get_setting_config, get_epochs); // Add button and text input + queue_manager->BuildTable("my_table"); // Add progress table + + + Doc("emp_test_container") + << queue_manager->GetDiv(); + + queue_manager->AddRun(get_setting_config(), get_epochs()); + queue_manager->AddNewQueuedRunToTable(); + + emp_assert(!queue_manager->IsEmpty()); + emp_assert(queue_manager->RunsRemaining() == 1); + emp_assert(queue_manager->FrontRun().GetEpoch() == 0); + emp_assert(queue_manager->FrontRun().GetConfig().GetValue("my_param") == 9); + emp_assert(queue_manager->FrontRun().GetNEpochs() == 50); + + queue_manager->FrontRun().IncEpoch(); + queue_manager->Update(); + + queue_manager->AddRun(config, 20); + queue_manager->AddNewQueuedRunToTable(); + + emp_assert(queue_manager->RunsRemaining() == 2); + queue_manager->FrontRun().IncEpoch(49); + queue_manager->Update(); + + // Previous run should have finished + emp_assert(queue_manager->RunsRemaining() == 1); + } + + ~Test_QueueManager() { + queue_manager.Delete(); + } + + void Describe() override { + + EM_ASM({ + describe("emp::QueueManager GUI", function() { + + describe("data table", function() { + + it('should exist and be a table', function() { + chai.assert.equal($( "table#my_table" ).length, 1); + }); + + it('should have grandparent #emp_test_container', function() { + const grand_parent_id = $("#my_table").parent().parent().attr("id"); + chai.assert.equal(grand_parent_id, "emp_test_container"); + }); + + it('should have 4 columns', function() { + columns = $("#my_table").find("th>span"); + chai.assert.equal(columns.length, 4); + chai.assert.equal(columns[0].firstChild.textContent, "Run"); + chai.assert.equal(columns[1].firstChild.textContent, "my_param"); + chai.assert.equal(columns[2].firstChild.textContent, "Epoch"); + chai.assert.equal(columns[3].firstChild.textContent, "Hello metric"); + }); + + it('should have 3 rows', function() { + rows = $("#my_table").find("tr"); + chai.assert.equal(rows.length, 3); + }); + + it('should have the correct data', function() { + rows = $("#my_table").find("td"); + chai.assert.equal(rows[0].children[0].firstChild.textContent, "0"); + chai.assert.equal(rows[1].children[0].firstChild.textContent, "9"); + chai.assert.equal(rows[2].children[0].firstChild.textContent, "50"); + chai.assert.equal(rows[3].children[0].firstChild.textContent, "Hello"); + chai.assert.equal(rows[4].children[0].firstChild.textContent, "1"); + chai.assert.equal(rows[5].children[0].firstChild.textContent, "6"); + chai.assert.equal(rows[6].children[0].firstChild.textContent, "Waiting..."); + chai.assert.equal(rows[7].children[0].firstChild.textContent, "Waiting..."); + }); + + + }); + + describe("Queue button", function() { + + it('button should exist', function() { + chai.assert.equal($( "button#queue_but" ).length, 1); + }); + + it('text area should exist', function() { + chai.assert.equal($( "textarea#run_count" ).length, 1); + }); + + it('should respond correctly to clicks', function() { + $( "button#queue_but" ).click(); + rows = $("#my_table").find("tr"); + chai.assert.equal(rows.length, 13); + }); + + }); + + }); + }); + } + +}; + +// Create a MochaTestRunner object in the global namespace so that it hangs around after main finishes. +emp::web::MochaTestRunner test_runner; + +int main() { + + // MochaTestRunner::Initialize will make sure empirical's web environment is initialized, and will + // append a set of div elements (with the given string ids) to the HTML document body. + // Between tests, the MochaTestRunner clears the contents of these div elements. + // Remember, karma is generating our HTML file, so this is useful for attaching any HTML divs that + // you want to interact with in your tests. + test_runner.Initialize({"emp_test_container"}); + + // We add tests to the test runner like this: + // where "Test Element" is the name of the test (and does not need to be unique) + test_runner.AddTest( + "Test QueueManager" + ); + + // Once we add all of the tests we want to run in this file, run them! + test_runner.Run(); +} From eb29b79f5625b436b11ec643331c1df3c9f29469 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Mon, 1 Mar 2021 02:44:56 -0500 Subject: [PATCH 06/19] Fix name collision issue in SettingConfig --- include/emp/config/SettingConfig.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/emp/config/SettingConfig.hpp b/include/emp/config/SettingConfig.hpp index 97d68ff80d..e013e79055 100644 --- a/include/emp/config/SettingConfig.hpp +++ b/include/emp/config/SettingConfig.hpp @@ -72,9 +72,9 @@ class SettingConfig { : SettingBase(_name, _desc, _flag, _arg), var_ptr(_var) { ; } emp::Ptr Clone() const override { - auto si_ptr = emp::NewPtr>(name, desc, flag, args_label); - si_ptr->value = this->value; - return si_ptr; + emp::Ptr> setting_info_ptr = emp::NewPtr>(name, desc, flag, args_label); + setting_info_ptr->value = this->value; + return setting_info_ptr; } size_t GetSize() const override { return 1; } From e221c997da65648258f065d52c89d1f5bdc7a519 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 2 Mar 2021 14:42:49 -0500 Subject: [PATCH 07/19] Get test coverage for QueueManager --- tests/web/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/web/Makefile b/tests/web/Makefile index 768461165f..a7647ddf50 100644 --- a/tests/web/Makefile +++ b/tests/web/Makefile @@ -4,7 +4,7 @@ TEST_NAMES = ConfigPanel Collapse LoadingModal Card CommentBox Modal ToggleSwitc # Currently a couple of the tests won't compile to native so this is a separate list for now. Eventually we should fix # that and just have one list -NATIVE_TEST_NAMES = ConfigPanel LoadingModal Card CommentBox Modal ToggleSwitch CodeBlock LoadingIcon FontAwesomeIcon ClickCounterDemo Element TextFeed js_utils JSWrap Widget +NATIVE_TEST_NAMES = ConfigPanel LoadingModal Card CommentBox Modal ToggleSwitch CodeBlock LoadingIcon FontAwesomeIcon ClickCounterDemo Element TextFeed js_utils JSWrap Widget QueueManager # Flags to use regardless of compiler CFLAGS_all := -Wall -Wno-unused-function -Wno-gnu-zero-variadic-macro-arguments -Wno-dollar-in-identifier-extension -std=c++17 -I../../include/ -I../../ From a440e47b9358e9be02e607fccd2d7c73f1e33a8e Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 2 Mar 2021 17:48:00 -0500 Subject: [PATCH 08/19] Fix bug where compiler was confused by has functions --- include/emp/datastructs/map_utils.hpp | 2 +- include/emp/datastructs/vector_utils.hpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/emp/datastructs/map_utils.hpp b/include/emp/datastructs/map_utils.hpp index 331ebe2104..da8bb60c80 100644 --- a/include/emp/datastructs/map_utils.hpp +++ b/include/emp/datastructs/map_utils.hpp @@ -21,7 +21,7 @@ namespace emp { /// Take any map type, and run find to determine if a key is present. template - inline bool Has( const MAP_T & in_map, const KEY_T & key ) { + inline auto Has( const MAP_T & in_map, const KEY_T & key ) -> emp::sfinae_decoy { return in_map.find(key) != in_map.end(); } diff --git a/include/emp/datastructs/vector_utils.hpp b/include/emp/datastructs/vector_utils.hpp index 6c52483851..0bc888fe7d 100644 --- a/include/emp/datastructs/vector_utils.hpp +++ b/include/emp/datastructs/vector_utils.hpp @@ -80,6 +80,12 @@ namespace emp { return FindValue(v, val) >= 0; } + // String version to fix template type deduction error + template + bool Has(const emp::vector & v, const std::string & val) { + return FindValue(v, val) >= 0; + } + /// Return number of times a value occurs in a vector template int Count(const emp::vector & vec, const T & val) { From 404dff48aaf9bd34500b668b00b500cc8b404aa2 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 2 Mar 2021 17:48:16 -0500 Subject: [PATCH 09/19] Improve queuemanager test coverage --- tests/web/QueueManager.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/web/QueueManager.cpp b/tests/web/QueueManager.cpp index ae3bea2844..811b6f393f 100644 --- a/tests/web/QueueManager.cpp +++ b/tests/web/QueueManager.cpp @@ -40,10 +40,12 @@ struct Test_QueueManager : public emp::web::BaseTest { queue_manager->AddQueueButton(get_setting_config, get_epochs); // Add button and text input queue_manager->BuildTable("my_table"); // Add progress table - Doc("emp_test_container") << queue_manager->GetDiv(); + // Test adding metric after table built + queue_manager->AddMetric(metric_fun, "Late Hello"); + queue_manager->AddRun(get_setting_config(), get_epochs()); queue_manager->AddNewQueuedRunToTable(); @@ -68,6 +70,7 @@ struct Test_QueueManager : public emp::web::BaseTest { } ~Test_QueueManager() { + queue_manager->ResetDiv(); queue_manager.Delete(); } @@ -89,11 +92,12 @@ struct Test_QueueManager : public emp::web::BaseTest { it('should have 4 columns', function() { columns = $("#my_table").find("th>span"); - chai.assert.equal(columns.length, 4); + chai.assert.equal(columns.length, 5); chai.assert.equal(columns[0].firstChild.textContent, "Run"); chai.assert.equal(columns[1].firstChild.textContent, "my_param"); chai.assert.equal(columns[2].firstChild.textContent, "Epoch"); chai.assert.equal(columns[3].firstChild.textContent, "Hello metric"); + chai.assert.equal(columns[4].firstChild.textContent, "Late Hello"); }); it('should have 3 rows', function() { @@ -107,10 +111,12 @@ struct Test_QueueManager : public emp::web::BaseTest { chai.assert.equal(rows[1].children[0].firstChild.textContent, "9"); chai.assert.equal(rows[2].children[0].firstChild.textContent, "50"); chai.assert.equal(rows[3].children[0].firstChild.textContent, "Hello"); - chai.assert.equal(rows[4].children[0].firstChild.textContent, "1"); - chai.assert.equal(rows[5].children[0].firstChild.textContent, "6"); - chai.assert.equal(rows[6].children[0].firstChild.textContent, "Waiting..."); + chai.assert.equal(rows[4].children[0].firstChild.textContent, "Hello"); + chai.assert.equal(rows[5].children[0].firstChild.textContent, "1"); + chai.assert.equal(rows[6].children[0].firstChild.textContent, "6"); chai.assert.equal(rows[7].children[0].firstChild.textContent, "Waiting..."); + chai.assert.equal(rows[8].children[0].firstChild.textContent, "Waiting..."); + chai.assert.equal(rows[9].children[0].firstChild.textContent, "Waiting..."); }); From ba0e45264b4f132411e111bbe2bd92dd55bc24f4 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 2 Mar 2021 17:48:34 -0500 Subject: [PATCH 10/19] Test settingconfig --- include/emp/config/SettingConfig.hpp | 10 ++++- tests/config/SettingConfig.cpp | 59 +++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/include/emp/config/SettingConfig.hpp b/include/emp/config/SettingConfig.hpp index e013e79055..3a4bb17cf5 100644 --- a/include/emp/config/SettingConfig.hpp +++ b/include/emp/config/SettingConfig.hpp @@ -30,7 +30,7 @@ namespace emp { /// those values for a factorial analysis. class SettingConfig { - private: + private: /// Base class to describe information about a single setting. struct SettingBase { std::string name; ///< Name for this setting @@ -109,6 +109,7 @@ class SettingConfig { emp::Ptr Clone() const override { auto csi_ptr = emp::NewPtr>(name, desc, flag, args_label); csi_ptr->values = this->values; + csi_ptr->id = this->id; return csi_ptr; } @@ -119,6 +120,13 @@ class SettingConfig { if (i) ss << ','; ss << values[i]; } + return ss.str(); + } + + std::string AsString(size_t id) const override { + std::stringstream ss; + ss << values[id]; + return ss.str(); } bool FromString(const std::string_view &input) override { diff --git a/tests/config/SettingConfig.cpp b/tests/config/SettingConfig.cpp index ae0675c2fe..676fdab819 100644 --- a/tests/config/SettingConfig.cpp +++ b/tests/config/SettingConfig.cpp @@ -2,8 +2,65 @@ #include "third-party/Catch/single_include/catch2/catch.hpp" +#include "emp/datastructs/vector_utils.hpp" #include "emp/config/SettingConfig.hpp" -TEST_CASE("Test SettingConfit", "[config]") +TEST_CASE("Test SettingConfig", "[config]") { +emp::SettingConfig config; + +config.AddSetting("num_runs") = 200; +config.AddComboSetting("pop_size") = { 100,200,400,800 }; + +// Test setup +emp::vector setting_names = config.GetSettingMapNames(); +CHECK(emp::Has(setting_names, "num_runs")); +CHECK(emp::Has(setting_names, "pop_size")); +auto setting_ptrs = config.GetSettingMapBase(); +CHECK(setting_names.size() == 2); + +// Test getting values +CHECK(config.GetValue("num_runs") == 200); +CHECK(config.GetValue("pop_size") == 100); +CHECK(config.MaxValue("pop_size") == 800); + +// Test parsing +emp::vector opts = {"example_prog", "example_unused", "--pop_size", "800,900,1000", "--num_runs", "500"}; +CHECK(config.ProcessOptions(opts)); +CHECK(config.HasUnusedArgs()); +CHECK(config.GetUnusedArgs()[0] == "example_unused"); +CHECK(config.GetExeName() == "example_prog"); +CHECK(!config.HasErrors()); +CHECK(config.CurSettings() == "500,800"); +CHECK(config.CountCombos() == 3); +CHECK(config.CurComboString() == "800"); +CHECK(config.GetComboHeaders() == "pop_size"); +CHECK(config.GetSettingHeaders() == "num_runs,pop_size"); +CHECK(config.GetValue("num_runs") == 500); +CHECK(config.GetValue("pop_size") == 800); + +// Test copy constructor +emp::SettingConfig other_config(config); +CHECK(other_config.HasUnusedArgs()); +CHECK(other_config.GetUnusedArgs()[0] == "example_unused"); +CHECK(other_config.GetExeName() == "example_prog"); +CHECK(!other_config.HasErrors()); +CHECK(other_config.CurSettings() == "500,800"); +CHECK(other_config.CountCombos() == 3); +CHECK(other_config.CurComboString() == "800"); +CHECK(other_config.GetComboHeaders() == "pop_size"); +CHECK(other_config.GetSettingHeaders() == "num_runs,pop_size"); +CHECK(other_config.GetValue("num_runs") == 500); +CHECK(other_config.GetValue("pop_size") == 800); + +// Test getting next combo +config.NextCombo(); +CHECK(config.CurSettings() == "500,900"); + +// Test actions +config.AddAction("test_action", "a test action", 'a', [](){std::cout << "An action happened!" << std::endl;}); + +// Test printing help +config.PrintHelp(); + } \ No newline at end of file From eee7bcc5b2dd4b24d30595ff9e6d6eddfb35b2ba Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 2 Mar 2021 17:58:07 -0500 Subject: [PATCH 11/19] Fix include on map utils --- include/emp/datastructs/map_utils.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/emp/datastructs/map_utils.hpp b/include/emp/datastructs/map_utils.hpp index da8bb60c80..067930bca3 100644 --- a/include/emp/datastructs/map_utils.hpp +++ b/include/emp/datastructs/map_utils.hpp @@ -16,6 +16,7 @@ #include "../base/map.hpp" #include "../base/vector.hpp" +#include "../meta/meta.hpp" namespace emp { From 02a27e7b15c8fc9ab92680c0a30471dcf30e5d4d Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Tue, 2 Mar 2021 19:23:11 -0500 Subject: [PATCH 12/19] Fix memory management error in copy constructor --- include/emp/config/SettingConfig.hpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/include/emp/config/SettingConfig.hpp b/include/emp/config/SettingConfig.hpp index 3a4bb17cf5..d414a335ea 100644 --- a/include/emp/config/SettingConfig.hpp +++ b/include/emp/config/SettingConfig.hpp @@ -71,8 +71,14 @@ class SettingConfig { emp::Ptr _var = nullptr) ///< Pointer to variable to set (optional) : SettingBase(_name, _desc, _flag, _arg), var_ptr(_var) { ; } + ~SettingInfo(){if(var_ptr){var_ptr.Delete();}} + emp::Ptr Clone() const override { - emp::Ptr> setting_info_ptr = emp::NewPtr>(name, desc, flag, args_label); + emp::Ptr new_var_ptr = nullptr; + if (var_ptr) { + new_var_ptr = NewPtr(*var_ptr); + } + emp::Ptr> setting_info_ptr = emp::NewPtr>(name, desc, flag, args_label, new_var_ptr); setting_info_ptr->value = this->value; return setting_info_ptr; } @@ -106,8 +112,14 @@ class SettingConfig { emp::Ptr _var = nullptr) ///< Pointer to variable to set (optional) : SettingBase(_name, _desc, _flag, _args), var_ptr(_var) { ; } + ~ComboSettingInfo(){if(var_ptr){var_ptr.Delete();}} + emp::Ptr Clone() const override { - auto csi_ptr = emp::NewPtr>(name, desc, flag, args_label); + emp::Ptr new_var_ptr = nullptr; + if (var_ptr) { + new_var_ptr = NewPtr(*var_ptr); + } + auto csi_ptr = emp::NewPtr>(name, desc, flag, args_label, new_var_ptr); csi_ptr->values = this->values; csi_ptr->id = this->id; return csi_ptr; @@ -194,12 +206,12 @@ class SettingConfig { SettingConfig(const SettingConfig &other) { combo_settings.resize(other.combo_settings.size()); - for (size_t i = 0; i < combo_settings.size(); i++) { - combo_settings[i] = other.combo_settings[i]->Clone(); - } for (const auto &entry : other.setting_map) { setting_map[entry.first] = other.setting_map.at(entry.first)->Clone(); } + for (size_t i = 0; i < combo_settings.size(); i++) { + combo_settings[i] = setting_map[other.combo_settings[i]->name]; + } action_map = other.action_map; cur_combo = other.cur_combo; From 47eb05bb91515ad3e364bd2a517c08bb3861979a Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 17 Mar 2021 00:15:15 -0400 Subject: [PATCH 13/19] Test fixing formatting --- include/emp/config/SettingConfig.hpp | 1035 ++++++++++++++------------ 1 file changed, 543 insertions(+), 492 deletions(-) diff --git a/include/emp/config/SettingConfig.hpp b/include/emp/config/SettingConfig.hpp index d414a335ea..086c7606e4 100644 --- a/include/emp/config/SettingConfig.hpp +++ b/include/emp/config/SettingConfig.hpp @@ -1,10 +1,12 @@ /** * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md + * @copyright Copyright (C) Michigan State University, MIT Software license; + * see doc/LICENSE.md * @date 2020 * * @file SettingConfig.hpp - * @brief A tool for collecting settings, including from files and the command line. + * @brief A tool for collecting settings, including from files and the command + * line. * @note Status: DEPRECATED! */ @@ -26,551 +28,600 @@ namespace emp { -/// Class to take a set of value for each "setting" and then step through all combinations of -/// those values for a factorial analysis. +/// Class to take a set of value for each "setting" and then step through all +/// combinations of those values for a factorial analysis. class SettingConfig { - private: - /// Base class to describe information about a single setting. - struct SettingBase { - std::string name; ///< Name for this setting - std::string desc; ///< Description of setting - char flag; ///< Command-line flag ('\0' for none) - std::string option; ///< Command-line longer option. - std::string args_label; ///< Label for option arguments (used in --help) - - SettingBase(const std::string &_name, const std::string &_desc, - const char _flag, const std::string &_args_label) - : name(_name), desc(_desc), flag(_flag), option(emp::to_string("--", _name)), args_label(_args_label) {} - virtual ~SettingBase() {} - - virtual emp::Ptr Clone() const = 0; - - virtual size_t GetSize() const = 0; ///< How many values are available? - virtual std::string AsString() const = 0; ///< All values, as a single string. - virtual std::string AsString(size_t) const = 0; ///< A specified value as a string. - virtual bool FromString(const std::string_view &) = 0; ///< Convert string to set of settings. - virtual bool SetValueID(size_t) { return false; } ///< Setup cur value in linked variable - virtual bool IsComboSetting() { return false; } ///< Do we have a combo setting? - virtual size_t GetID() const { return (size_t)-1; } ///< Combination ID for this setting. - - bool IsOptionMatch(const std::string &test_option) const { return test_option == option; } - bool IsFlagMatch(const char test_flag) const { return test_flag == flag; } - }; - - /// Full details about a single setting, including type information and values. - template - struct SettingInfo : public SettingBase { - T value; - emp::Ptr var_ptr = nullptr; - - SettingInfo(const std::string &_name, ///< Unique name for this setting. - const std::string &_desc, ///< Description of this setting (for help) - const char _flag, ///< Single char flag for easy access (e.g., "-h") - const std::string &_arg, ///< Label for option argument (for help) - emp::Ptr _var = nullptr) ///< Pointer to variable to set (optional) - : SettingBase(_name, _desc, _flag, _arg), var_ptr(_var) { ; } - - ~SettingInfo(){if(var_ptr){var_ptr.Delete();}} - - emp::Ptr Clone() const override { - emp::Ptr new_var_ptr = nullptr; - if (var_ptr) { - new_var_ptr = NewPtr(*var_ptr); - } - emp::Ptr> setting_info_ptr = emp::NewPtr>(name, desc, flag, args_label, new_var_ptr); - setting_info_ptr->value = this->value; - return setting_info_ptr; - } - - size_t GetSize() const override { return 1; } - std::string AsString() const override { return emp::to_string(value); } - std::string AsString(size_t id) const override { - emp_assert(id == 0); - return emp::to_string(value); - } - - bool FromString(const std::string_view &input) override { - value = emp::from_string(input); - // @CAO: Could do more tests to make sure whole string was used. - if (!var_ptr.IsNull()) *var_ptr = value; - return true; - } - }; - - /// Allow a single setting to have multiple values specified that should be stepped through. - template - struct ComboSettingInfo : public SettingBase { - emp::vector values; ///< Set of values to use for this setting. - emp::Ptr var_ptr = nullptr; ///< Pointer to variable to set as combos change. - size_t id; ///< Unique ID/position for this setting. - - ComboSettingInfo(const std::string &_name, ///< Unique name for this setting. - const std::string &_desc, ///< Description of this setting (for help) - const char _flag, ///< Char flag for easy access (e.g., "-h") - const std::string &_args, ///< Label for option arguments (for help) - emp::Ptr _var = nullptr) ///< Pointer to variable to set (optional) - : SettingBase(_name, _desc, _flag, _args), var_ptr(_var) { ; } - - ~ComboSettingInfo(){if(var_ptr){var_ptr.Delete();}} - - emp::Ptr Clone() const override { - emp::Ptr new_var_ptr = nullptr; - if (var_ptr) { - new_var_ptr = NewPtr(*var_ptr); - } - auto csi_ptr = emp::NewPtr>(name, desc, flag, args_label, new_var_ptr); - csi_ptr->values = this->values; - csi_ptr->id = this->id; - return csi_ptr; - } - - size_t GetSize() const override { return values.size(); } - std::string AsString() const override { - std::stringstream ss; - for (size_t i = 0; i < values.size(); i++) { - if (i) ss << ','; - ss << values[i]; - } - return ss.str(); - } - - std::string AsString(size_t id) const override { - std::stringstream ss; - ss << values[id]; - return ss.str(); - } - - bool FromString(const std::string_view &input) override { - // Divide up inputs into comma-separated units. - auto slices = emp::slice(input, ','); - - // Clear out the values to set, one at a time (in most cases, aleady clear) - values.resize(0); - - // Process each slice into one or more values. - for (auto &cur_str : slices) { - // If we are working with an arithmetic type, check if this is a range. - if constexpr (std::is_arithmetic::value) { - auto r_slices = emp::slice(cur_str, ':'); - if (r_slices.size() > 3) return false; // Error! Too many slices!!! - T start = emp::from_string(r_slices[0]); - T end = emp::from_string(r_slices.back()); // Same as start if one value. - T step = (r_slices.size() == 3) ? emp::from_string(r_slices[1]) : 1; - while (start <= end) { - values.push_back(start); - start += step; - } - } - - // Otherwise do a direct conversion. - else { - values.push_back(emp::from_string(cur_str)); - } - } - - if (!var_ptr.IsNull() && values.size()) *var_ptr = values[0]; - return values.size(); // Must result in at least one value. - } - - bool SetValueID(size_t id) override { - if (var_ptr) *var_ptr = values[id]; - return true; - } - bool IsComboSetting() override { return true; } - size_t GetID() const override { return id; } - }; - - /// A setting that is just a flag with an action function to run if it's called. - struct ActionFlag { - std::string name; ///< Name for this flag - std::string desc; ///< Description of flag - char flag; ///< Command-line flag ('\0' for none) - std::function fun; ///< Function to be called if flag is set. - }; - - std::string exe_name = ""; - - std::map> setting_map; ///< All settings by name - std::map action_map; ///< Action flags - - emp::vector> combo_settings; ///< Multi-value settings (in order) - emp::vector cur_combo; ///< Which combo settings are we currently using? - size_t combo_id = 0; ///< Unique value indicating which combination we are on. - - emp::vector unused_args; // Arguments that we were unable to process. - std::string errors; - - public: - SettingConfig() = default; - - SettingConfig(const SettingConfig &other) { - combo_settings.resize(other.combo_settings.size()); - for (const auto &entry : other.setting_map) { - setting_map[entry.first] = other.setting_map.at(entry.first)->Clone(); - } - for (size_t i = 0; i < combo_settings.size(); i++) { - combo_settings[i] = setting_map[other.combo_settings[i]->name]; - } +private: + /// Base class to describe information about a single setting. + struct SettingBase { + std::string name; ///< Name for this setting + std::string desc; ///< Description of setting + char flag; ///< Command-line flag ('\0' for none) + std::string option; ///< Command-line longer option. + std::string args_label; ///< Label for option arguments (used in --help) + + SettingBase(const std::string &_name, const std::string &_desc, + const char _flag, const std::string &_args_label) + : name(_name), desc(_desc), flag(_flag), + option(emp::to_string("--", _name)), args_label(_args_label) {} + virtual ~SettingBase() {} + + virtual emp::Ptr Clone() const = 0; + + virtual size_t GetSize() const = 0; ///< How many values are available? + virtual std::string + AsString() const = 0; ///< All values, as a single string. + virtual std::string + AsString(size_t) const = 0; ///< A specified value as a string. + virtual bool FromString( + const std::string_view &) = 0; ///< Convert string to set of settings. + virtual bool SetValueID(size_t) { + return false; + } ///< Setup cur value in linked variable + virtual bool IsComboSetting() { + return false; + } ///< Do we have a combo setting? + virtual size_t GetID() const { + return (size_t)-1; + } ///< Combination ID for this setting. + + bool IsOptionMatch(const std::string &test_option) const { + return test_option == option; + } + bool IsFlagMatch(const char test_flag) const { return test_flag == flag; } + }; + + /// Full details about a single setting, including type information and + /// values. + template struct SettingInfo : public SettingBase { + T value; + emp::Ptr var_ptr = nullptr; + + SettingInfo( + const std::string &_name, ///< Unique name for this setting. + const std::string &_desc, ///< Description of this setting (for help) + const char _flag, ///< Single char flag for easy access (e.g., "-h") + const std::string &_arg, ///< Label for option argument (for help) + emp::Ptr _var = nullptr) ///< Pointer to variable to set (optional) + : SettingBase(_name, _desc, _flag, _arg), var_ptr(_var) { + ; + } - action_map = other.action_map; - cur_combo = other.cur_combo; - combo_id = other.combo_id; - unused_args = other.unused_args; - errors = other.errors; - exe_name = other.exe_name; + ~SettingInfo() { + if (var_ptr) { + var_ptr.Delete(); + } } - ~SettingConfig() { - for (auto [name, ptr] : setting_map) ptr.Delete(); + emp::Ptr Clone() const override { + emp::Ptr new_var_ptr = nullptr; + if (var_ptr) { + new_var_ptr = NewPtr(*var_ptr); + } + emp::Ptr> setting_info_ptr = emp::NewPtr>( + name, desc, flag, args_label, new_var_ptr); + setting_info_ptr->value = this->value; + return setting_info_ptr; } - const std::string &GetExeName() const { return exe_name; } - size_t GetComboID() const { return combo_id; } - const emp::vector &GetUnusedArgs() const { return unused_args; } - const std::string &GetErrors() const { return errors; } + size_t GetSize() const override { return 1; } + std::string AsString() const override { return emp::to_string(value); } + std::string AsString(size_t id) const override { + emp_assert(id == 0); + return emp::to_string(value); + } - bool HasUnusedArgs() const { return unused_args.size(); } - bool HasErrors() const { return errors.size(); } + bool FromString(const std::string_view &input) override { + value = emp::from_string(input); + // @CAO: Could do more tests to make sure whole string was used. + if (!var_ptr.IsNull()) + *var_ptr = value; + return true; + } + }; + + /// Allow a single setting to have multiple values specified that should be + /// stepped through. + template struct ComboSettingInfo : public SettingBase { + emp::vector values; ///< Set of values to use for this setting. + emp::Ptr var_ptr = + nullptr; ///< Pointer to variable to set as combos change. + size_t id; ///< Unique ID/position for this setting. + + ComboSettingInfo( + const std::string &_name, ///< Unique name for this setting. + const std::string &_desc, ///< Description of this setting (for help) + const char _flag, ///< Char flag for easy access (e.g., "-h") + const std::string &_args, ///< Label for option arguments (for help) + emp::Ptr _var = nullptr) ///< Pointer to variable to set (optional) + : SettingBase(_name, _desc, _flag, _args), var_ptr(_var) { + ; + } - /// Retrieves all of the setting names in config and places into std::vector - std::vector GetSettingMapNames() const { - std::vector result; - for (const auto &p : setting_map) { - result.push_back(p.first); - } - return result; + ~ComboSettingInfo() { + if (var_ptr) { + var_ptr.Delete(); + } } - /// Retrieves all of the setting names in config and places into std::vector - std::vector> GetSettingMapBase() const { - std::vector> result; - for (const auto &p : setting_map) { - result.push_back(p.second); - } - return result; + emp::Ptr Clone() const override { + emp::Ptr new_var_ptr = nullptr; + if (var_ptr) { + new_var_ptr = NewPtr(*var_ptr); + } + auto csi_ptr = emp::NewPtr>(name, desc, flag, + args_label, new_var_ptr); + csi_ptr->values = this->values; + csi_ptr->id = this->id; + return csi_ptr; } - /// Get the current value of a specified setting. - template - const T &GetValue(const std::string &name) const { - emp_assert(emp::Has(setting_map, name), name); - emp::Ptr base_ptr = setting_map.find(name)->second; - - // If we have a combo setting, determine the current value. - if (base_ptr->IsComboSetting()) { - emp::Ptr> ptr = base_ptr.Cast>(); - size_t id = cur_combo[ptr->id]; - return ptr->values[id]; - } + size_t GetSize() const override { return values.size(); } + std::string AsString() const override { + std::stringstream ss; + for (size_t i = 0; i < values.size(); i++) { + if (i) + ss << ','; + ss << values[i]; + } + return ss.str(); + } - // Otherwise we have a regular setting. - return base_ptr.Cast>()->value; + std::string AsString(size_t id) const override { + std::stringstream ss; + ss << values[id]; + return ss.str(); } - /// Scan through all values and return the maximum. - template - T MaxValue(const std::string &name) const { - emp_assert(emp::Has(setting_map, name), name); - emp::Ptr base_ptr = setting_map.find(name)->second; + bool FromString(const std::string_view &input) override { + // Divide up inputs into comma-separated units. + auto slices = emp::slice(input, ','); + + // Clear out the values to set, one at a time (in most cases, aleady + // clear) + values.resize(0); + + // Process each slice into one or more values. + for (auto &cur_str : slices) { + // If we are working with an arithmetic type, check if this is a range. + if constexpr (std::is_arithmetic::value) { + auto r_slices = emp::slice(cur_str, ':'); + if (r_slices.size() > 3) + return false; // Error! Too many slices!!! + T start = emp::from_string(r_slices[0]); + T end = emp::from_string( + r_slices.back()); // Same as start if one value. + T step = + (r_slices.size() == 3) ? emp::from_string(r_slices[1]) : 1; + while (start <= end) { + values.push_back(start); + start += step; + } + } - // If we have a combo setting, determine the current value. - if (base_ptr->IsComboSetting()) { - emp::Ptr> ptr = base_ptr.Cast>(); - return emp::FindMax(ptr->values); + // Otherwise do a direct conversion. + else { + values.push_back(emp::from_string(cur_str)); } + } - // Otherwise we have a regular setting with just one value. - return base_ptr.Cast>()->value; + if (!var_ptr.IsNull() && values.size()) + *var_ptr = values[0]; + return values.size(); // Must result in at least one value. } - /// Add a new setting of a specified type. Returns the (initially empty) vector of values - /// to allow easy setting. - /// Example: - /// config.AddSetting("num_runs") = 200; - - template - T &AddSetting(const std::string &name, - const std::string &desc, - const char option_flag, - T &var, - const std::string &args_label = "Value") { - emp_assert(!emp::Has(setting_map, name)); - auto new_ptr = emp::NewPtr>(name, desc, option_flag, args_label, &var); - setting_map[name] = new_ptr; - return new_ptr->value; + bool SetValueID(size_t id) override { + if (var_ptr) + *var_ptr = values[id]; + return true; } - - /// Add a new setting not linked to a variable - - template - T &AddSetting(const std::string &name, - const std::string &desc = "", - const char option_flag = '\0') { - emp_assert(!emp::Has(setting_map, name)); - auto new_ptr = emp::NewPtr>(name, desc, option_flag, "Value"); - setting_map[name] = new_ptr; - return new_ptr->value; + bool IsComboSetting() override { return true; } + size_t GetID() const override { return id; } + }; + + /// A setting that is just a flag with an action function to run if it's + /// called. + struct ActionFlag { + std::string name; ///< Name for this flag + std::string desc; ///< Description of flag + char flag; ///< Command-line flag ('\0' for none) + std::function fun; ///< Function to be called if flag is set. + }; + + std::string exe_name = ""; + + std::map> + setting_map; ///< All settings by name + std::map action_map; ///< Action flags + + emp::vector> + combo_settings; ///< Multi-value settings (in order) + emp::vector + cur_combo; ///< Which combo settings are we currently using? + size_t combo_id = 0; ///< Unique value indicating which combination we are on. + + emp::vector + unused_args; // Arguments that we were unable to process. + std::string errors; + +public: + SettingConfig() = default; + + SettingConfig(const SettingConfig &other) { + combo_settings.resize(other.combo_settings.size()); + for (const auto &entry : other.setting_map) { + setting_map[entry.first] = other.setting_map.at(entry.first)->Clone(); } - - /// Add a new setting of a specified type. Returns the (initially empty) vector of values - /// to allow easy setting. - /// Example: - /// config.AddComboSetting("pop_size") = { 100,200,400,800 }; - - template - emp::vector &AddComboSetting(const std::string &name, - const std::string &desc = "", - const char option_flag = '\0') { - emp_assert(!emp::Has(setting_map, name)); - auto new_ptr = emp::NewPtr>(name, desc, option_flag, "Values..."); - new_ptr->id = combo_settings.size(); - combo_settings.push_back(new_ptr); - setting_map[name] = new_ptr; - cur_combo.push_back(0); - return new_ptr->values; + for (size_t i = 0; i < combo_settings.size(); i++) { + combo_settings[i] = setting_map[other.combo_settings[i]->name]; } - /// A setting can also be linked to a value that is kept up-to-date. - template - emp::vector &AddComboSetting(const std::string &name, - const std::string &desc, - const char option_flag, - T &var, - const std::string &args_label = "Values...") { - emp_assert(!emp::Has(setting_map, name)); - auto new_ptr = emp::NewPtr>(name, desc, option_flag, args_label, &var); - new_ptr->id = combo_settings.size(); - combo_settings.push_back(new_ptr); - setting_map[name] = new_ptr; - cur_combo.push_back(0); - return new_ptr->values; + action_map = other.action_map; + cur_combo = other.cur_combo; + combo_id = other.combo_id; + unused_args = other.unused_args; + errors = other.errors; + exe_name = other.exe_name; + } + + ~SettingConfig() { + for (auto [name, ptr] : setting_map) + ptr.Delete(); + } + + const std::string &GetExeName() const { return exe_name; } + size_t GetComboID() const { return combo_id; } + const emp::vector &GetUnusedArgs() const { return unused_args; } + const std::string &GetErrors() const { return errors; } + + bool HasUnusedArgs() const { return unused_args.size(); } + bool HasErrors() const { return errors.size(); } + + /// Retrieves all of the setting names in config and places into std::vector + std::vector GetSettingMapNames() const { + std::vector result; + for (const auto &p : setting_map) { + result.push_back(p.first); } - - void AddAction(const std::string &name, - const std::string &desc, - const char flag, - std::function fun) { - std::string name_option = emp::to_string("--", name); - std::string flag_option = emp::to_string("-", flag); - emp_assert(!emp::Has(action_map, name_option)); - emp_assert(!emp::Has(action_map, flag_option)); - action_map[name_option] = ActionFlag{name, desc, flag, fun}; - action_map[flag_option] = ActionFlag{name, desc, flag, fun}; + return result; + } + + /// Retrieves all of the setting names in config and places into std::vector + std::vector> GetSettingMapBase() const { + std::vector> result; + for (const auto &p : setting_map) { + result.push_back(p.second); } - - /// Access ALL values for a specified setting, to be modified freely. - template - emp::vector &ComboValues(const std::string &name) { - emp_assert(emp::Has(setting_map, name), name); - emp_assert(setting_map[name]->IsComboSetting()); - return setting_map[name].DynamicCast>()->values; + return result; + } + + /// Get the current value of a specified setting. + template const T &GetValue(const std::string &name) const { + emp_assert(emp::Has(setting_map, name), name); + emp::Ptr base_ptr = setting_map.find(name)->second; + + // If we have a combo setting, determine the current value. + if (base_ptr->IsComboSetting()) { + emp::Ptr> ptr = base_ptr.Cast>(); + size_t id = cur_combo[ptr->id]; + return ptr->values[id]; } - /// Start over stepping through all combinations of parameter values. - void ResetCombos() { - // Setup as base combo. - for (size_t &x : cur_combo) x = 0; - combo_id = 0; + // Otherwise we have a regular setting. + return base_ptr.Cast>()->value; + } - // Setup all linked values. - for (auto x : combo_settings) x->SetValueID(0); - } - - /// Add a single new value to the specified setting. - template - void AddComboValue(const std::string &name, T &&val) { - emp_assert(emp::Has(setting_map, name), name); - emp_assert(setting_map[name]->IsComboSetting()); - auto ptr = setting_map[name].DynamicCast>(); - ptr->values.emplace_back(std::forward(val)); - } + /// Scan through all values and return the maximum. + template T MaxValue(const std::string &name) const { + emp_assert(emp::Has(setting_map, name), name); + emp::Ptr base_ptr = setting_map.find(name)->second; - /// Set all values for the specified setting. - template - void SetComboValues(const std::string &name, T1 &&val1, Ts &&... vals) { - emp_assert(emp::Has(setting_map, name)); - auto ptr = setting_map[name].DynamicCast>(); - emp::Append(ptr->values, std::forward(val1), std::forward(vals)...); + // If we have a combo setting, determine the current value. + if (base_ptr->IsComboSetting()) { + emp::Ptr> ptr = base_ptr.Cast>(); + return emp::FindMax(ptr->values); } - /// Determine how many unique combinations there currently are. - size_t CountCombos() { - size_t result = 1; - for (auto ptr : combo_settings) result *= ptr->GetSize(); - return result; - } - - /// Set the next combination of settings to be active. Return true if successful - /// or false if we ran through all combinations and reset. - bool NextCombo() { - combo_id++; - for (size_t i = 0; i < cur_combo.size(); i++) { - cur_combo[i]++; - - // Check if this new combo is valid. - if (cur_combo[i] < combo_settings[i]->GetSize()) { - combo_settings[i]->SetValueID(cur_combo[i]); // Set value in linked variable. - return true; - } - - // Since it's not, prepare to move on to the next one. - cur_combo[i] = 0; - combo_settings[i]->SetValueID(0); + // Otherwise we have a regular setting with just one value. + return base_ptr.Cast>()->value; + } + + /// Add a new setting of a specified type. Returns the (initially empty) + /// vector of values to allow easy setting. Example: + /// config.AddSetting("num_runs") = 200; + + template + T &AddSetting(const std::string &name, const std::string &desc, + const char option_flag, T &var, + const std::string &args_label = "Value") { + emp_assert(!emp::Has(setting_map, name)); + auto new_ptr = + emp::NewPtr>(name, desc, option_flag, args_label, &var); + setting_map[name] = new_ptr; + return new_ptr->value; + } + + /// Add a new setting not linked to a variable + + template + T &AddSetting(const std::string &name, const std::string &desc = "", + const char option_flag = '\0') { + emp_assert(!emp::Has(setting_map, name)); + auto new_ptr = + emp::NewPtr>(name, desc, option_flag, "Value"); + setting_map[name] = new_ptr; + return new_ptr->value; + } + + /// Add a new setting of a specified type. Returns the (initially empty) + /// vector of values to allow easy setting. Example: + /// config.AddComboSetting("pop_size") = { 100,200,400,800 }; + + template + emp::vector &AddComboSetting(const std::string &name, + const std::string &desc = "", + const char option_flag = '\0') { + emp_assert(!emp::Has(setting_map, name)); + auto new_ptr = + emp::NewPtr>(name, desc, option_flag, "Values..."); + new_ptr->id = combo_settings.size(); + combo_settings.push_back(new_ptr); + setting_map[name] = new_ptr; + cur_combo.push_back(0); + return new_ptr->values; + } + + /// A setting can also be linked to a value that is kept up-to-date. + template + emp::vector &AddComboSetting(const std::string &name, + const std::string &desc, + const char option_flag, T &var, + const std::string &args_label = "Values...") { + emp_assert(!emp::Has(setting_map, name)); + auto new_ptr = emp::NewPtr>(name, desc, option_flag, + args_label, &var); + new_ptr->id = combo_settings.size(); + combo_settings.push_back(new_ptr); + setting_map[name] = new_ptr; + cur_combo.push_back(0); + return new_ptr->values; + } + + void AddAction(const std::string &name, const std::string &desc, + const char flag, std::function fun) { + std::string name_option = emp::to_string("--", name); + std::string flag_option = emp::to_string("-", flag); + emp_assert(!emp::Has(action_map, name_option)); + emp_assert(!emp::Has(action_map, flag_option)); + action_map[name_option] = ActionFlag{name, desc, flag, fun}; + action_map[flag_option] = ActionFlag{name, desc, flag, fun}; + } + + /// Access ALL values for a specified setting, to be modified freely. + template emp::vector &ComboValues(const std::string &name) { + emp_assert(emp::Has(setting_map, name), name); + emp_assert(setting_map[name]->IsComboSetting()); + return setting_map[name].DynamicCast>()->values; + } + + /// Start over stepping through all combinations of parameter values. + void ResetCombos() { + // Setup as base combo. + for (size_t &x : cur_combo) + x = 0; + combo_id = 0; + + // Setup all linked values. + for (auto x : combo_settings) + x->SetValueID(0); + } + + /// Add a single new value to the specified setting. + template void AddComboValue(const std::string &name, T &&val) { + emp_assert(emp::Has(setting_map, name), name); + emp_assert(setting_map[name]->IsComboSetting()); + auto ptr = setting_map[name].DynamicCast>(); + ptr->values.emplace_back(std::forward(val)); + } + + /// Set all values for the specified setting. + template + void SetComboValues(const std::string &name, T1 &&val1, Ts &&...vals) { + emp_assert(emp::Has(setting_map, name)); + auto ptr = setting_map[name].DynamicCast>(); + emp::Append(ptr->values, std::forward(val1), std::forward(vals)...); + } + + /// Determine how many unique combinations there currently are. + size_t CountCombos() { + size_t result = 1; + for (auto ptr : combo_settings) + result *= ptr->GetSize(); + return result; + } + + /// Set the next combination of settings to be active. Return true if + /// successful or false if we ran through all combinations and reset. + bool NextCombo() { + combo_id++; + for (size_t i = 0; i < cur_combo.size(); i++) { + cur_combo[i]++; + + // Check if this new combo is valid. + if (cur_combo[i] < combo_settings[i]->GetSize()) { + combo_settings[i]->SetValueID( + cur_combo[i]); // Set value in linked variable. + return true; } - // No valid combo found. - combo_id = 0; - return false; + // Since it's not, prepare to move on to the next one. + cur_combo[i] = 0; + combo_settings[i]->SetValueID(0); } - /// Get the set of headers used for the CSV file. - std::string GetSettingHeaders(const std::string &separator = ",") { - std::string out_str; - for (auto [name, ptr] : setting_map) { - if (out_str.size()) out_str += separator; - out_str += ptr->name; - } - return out_str; + // No valid combo found. + combo_id = 0; + return false; + } + + /// Get the set of headers used for the CSV file. + std::string GetSettingHeaders(const std::string &separator = ",") { + std::string out_str; + for (auto [name, ptr] : setting_map) { + if (out_str.size()) + out_str += separator; + out_str += ptr->name; } - - /// Convert all of the current values into a comma-separated string. - std::string CurSettings(const std::string &separator = ",") const { - std::string out_str; - for (auto [name, ptr] : setting_map) { - if (ptr) { - if (out_str.size()) out_str += separator; - out_str += ptr->IsComboSetting() ? ptr->AsString(cur_combo[ptr->GetID()]) : ptr->AsString(); - } - } - return out_str; + return out_str; + } + + /// Convert all of the current values into a comma-separated string. + std::string CurSettings(const std::string &separator = ",") const { + std::string out_str; + for (auto [name, ptr] : setting_map) { + if (ptr) { + if (out_str.size()) + out_str += separator; + out_str += ptr->IsComboSetting() + ? ptr->AsString(cur_combo[ptr->GetID()]) + : ptr->AsString(); + } } - - /// Get the set of headers used for the CSV file. - std::string GetComboHeaders(const std::string &separator = ",") { - std::string out_string; - for (size_t i = 0; i < combo_settings.size(); i++) { - if (i) out_string += separator; - out_string += combo_settings[i]->name; - } - return out_string; + return out_str; + } + + /// Get the set of headers used for the CSV file. + std::string GetComboHeaders(const std::string &separator = ",") { + std::string out_string; + for (size_t i = 0; i < combo_settings.size(); i++) { + if (i) + out_string += separator; + out_string += combo_settings[i]->name; } - - /// Convert all of the current values into a comma-separated string. - std::string CurComboString(const std::string &separator = ",", - bool use_labels = false, ///< Print name with each value? - bool multi_only = false ///< Only print values that can change? - ) const { - std::string out_str; - for (size_t i = 0; i < cur_combo.size(); i++) { - if (multi_only && combo_settings[i]->GetSize() == 1) continue; - if (out_str.size()) out_str += separator; - if (use_labels) out_str += emp::to_string(combo_settings[i]->name, '='); - out_str += combo_settings[i]->AsString(cur_combo[i]); - } - return out_str; + return out_string; + } + + /// Convert all of the current values into a comma-separated string. + std::string + CurComboString(const std::string &separator = ",", + bool use_labels = false, ///< Print name with each value? + bool multi_only = false ///< Only print values that can change? + ) const { + std::string out_str; + for (size_t i = 0; i < cur_combo.size(); i++) { + if (multi_only && combo_settings[i]->GetSize() == 1) + continue; + if (out_str.size()) + out_str += separator; + if (use_labels) + out_str += emp::to_string(combo_settings[i]->name, '='); + out_str += combo_settings[i]->AsString(cur_combo[i]); } - - /// Scan through all settings for a match option and return ID. - emp::Ptr FindOptionMatch(const std::string &option_name) { - for (const auto &[name, ptr] : setting_map) { - if (ptr->IsOptionMatch(option_name)) return ptr; - } - return nullptr; + return out_str; + } + + /// Scan through all settings for a match option and return ID. + emp::Ptr FindOptionMatch(const std::string &option_name) { + for (const auto &[name, ptr] : setting_map) { + if (ptr->IsOptionMatch(option_name)) + return ptr; } - - /// Scan through all settings for a match option and return ID. - emp::Ptr FindFlagMatch(const char symbol) { - for (const auto &[name, ptr] : setting_map) { - if (ptr->IsFlagMatch(symbol)) return ptr; - } - return nullptr; + return nullptr; + } + + /// Scan through all settings for a match option and return ID. + emp::Ptr FindFlagMatch(const char symbol) { + for (const auto &[name, ptr] : setting_map) { + if (ptr->IsFlagMatch(symbol)) + return ptr; } + return nullptr; + } + + /// Take an input set of config options, process them, and track set of + /// unprocessed ones. + bool ProcessOptions(const emp::vector &args) { + exe_name = args[0]; + + for (size_t i = 1; i < args.size(); i++) { + const std::string &cur_arg = args[i]; + if (cur_arg.size() < 2 || cur_arg[0] != '-') { + unused_args.push_back(cur_arg); + continue; // If isn't an option, continue. + } - /// Take an input set of config options, process them, and track set of unprocessed ones. - bool ProcessOptions(const emp::vector &args) { - exe_name = args[0]; - - for (size_t i = 1; i < args.size(); i++) { - const std::string &cur_arg = args[i]; - if (cur_arg.size() < 2 || cur_arg[0] != '-') { - unused_args.push_back(cur_arg); - continue; // If isn't an option, continue. - } - - // See if this is a fully spelled-out option. - auto setting_ptr = FindOptionMatch(cur_arg); - if (!setting_ptr.IsNull()) { - if (++i >= args.size()) { - errors += "ERROR: Must provide args for option '--"; - errors += setting_ptr->name; - errors += "' to use!\n"; - std::cerr << errors; - return false; - } - setting_ptr->FromString(args[i]); - // @CAO: Should make sure string translated correctly. + // See if this is a fully spelled-out option. + auto setting_ptr = FindOptionMatch(cur_arg); + if (!setting_ptr.IsNull()) { + if (++i >= args.size()) { + errors += "ERROR: Must provide args for option '--"; + errors += setting_ptr->name; + errors += "' to use!\n"; + std::cerr << errors; + return false; } + setting_ptr->FromString(args[i]); + // @CAO: Should make sure string translated correctly. + } - // See if we have a flag option. - setting_ptr = FindFlagMatch(cur_arg[1]); - if (!setting_ptr.IsNull()) { - // Check if the flag is followed by the values without whitespace. - if (cur_arg.size() > 2) { - setting_ptr->FromString( emp::view_string(cur_arg,2) ); - } - else if (++i >= args.size()) { - std::cout << "ERROR: Must provide args to use!\n"; - return false; - } - else { - setting_ptr->FromString(args[i]); - } + // See if we have a flag option. + setting_ptr = FindFlagMatch(cur_arg[1]); + if (!setting_ptr.IsNull()) { + // Check if the flag is followed by the values without whitespace. + if (cur_arg.size() > 2) { + setting_ptr->FromString(emp::view_string(cur_arg, 2)); + } else if (++i >= args.size()) { + std::cout << "ERROR: Must provide args to use!\n"; + return false; + } else { + setting_ptr->FromString(args[i]); } + } - // Or see of this is a flag trigger. - else if (Has(action_map, cur_arg)) { - action_map[cur_arg].fun(); - } - - // Otherwise this argument will go unused; send it back. - else - unused_args.push_back(cur_arg); - } + // Or see of this is a flag trigger. + else if (Has(action_map, cur_arg)) { + action_map[cur_arg].fun(); + } - return true; + // Otherwise this argument will go unused; send it back. + else + unused_args.push_back(cur_arg); } - bool ProcessOptions(int argc, char *argv[]) { - return ProcessOptions(emp::cl::args_to_strings(argc, argv)); + return true; + } + + bool ProcessOptions(int argc, char *argv[]) { + return ProcessOptions(emp::cl::args_to_strings(argc, argv)); + } + + template void PrintHelp(const Ts &...examples) const { + std::cout << "Format: " << exe_name << " [OPTIONS...]\n" + << "\nSetting Options:\n"; + for (auto [name, ptr] : setting_map) { + std::string spacing(emp::Max(1, 12 - (int)ptr->args_label.size()), ' '); + std::cout << " -" << ptr->flag << " [" << ptr->args_label << "]" + << spacing << ": " << ptr->desc << " (--" << name << ") [" + << ptr->AsString() << "]\n"; } - template - void PrintHelp(const Ts &... examples) const { - std::cout << "Format: " << exe_name << " [OPTIONS...]\n" - << "\nSetting Options:\n"; - for (auto [name, ptr] : setting_map) { - std::string spacing(emp::Max(1, 12 - (int)ptr->args_label.size()), ' '); - std::cout << " -" << ptr->flag << " [" << ptr->args_label << "]" << spacing << ": " - << ptr->desc << " (--" << name << ") [" - << ptr->AsString() << "]\n"; - } - - std::cout << "\nAction Options:\n"; - for (auto [name, action] : action_map) { - if (name.size() == 2) continue; // Skip flag entries. - std::cout << " -" << action.flag << " : " - << action.desc << " (" << name << ")\n"; - } - - if constexpr (sizeof...(examples) > 0) { - std::cout << "\nExample: " << emp::to_string(examples...) << std::endl; - } + std::cout << "\nAction Options:\n"; + for (auto [name, action] : action_map) { + if (name.size() == 2) + continue; // Skip flag entries. + std::cout << " -" << action.flag << " : " << action.desc << " (" << name + << ")\n"; + } - std::cout.flush(); + if constexpr (sizeof...(examples) > 0) { + std::cout << "\nExample: " << emp::to_string(examples...) << std::endl; } -}; // namespace emp -} // namespace emp + std::cout.flush(); + } +}; // namespace emp + +} // namespace emp #endif From ab22149a19b1ef4b1a7e9d7f7370e0dd964fe51a Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Wed, 17 Mar 2021 00:36:36 -0400 Subject: [PATCH 14/19] Actually fix whitespace --- include/emp/config/SettingConfig.hpp | 1039 ++++++++++++-------------- 1 file changed, 495 insertions(+), 544 deletions(-) diff --git a/include/emp/config/SettingConfig.hpp b/include/emp/config/SettingConfig.hpp index 086c7606e4..0d17e60e6f 100644 --- a/include/emp/config/SettingConfig.hpp +++ b/include/emp/config/SettingConfig.hpp @@ -1,22 +1,20 @@ /** * @note This file is part of Empirical, https://github.com/devosoft/Empirical - * @copyright Copyright (C) Michigan State University, MIT Software license; - * see doc/LICENSE.md + * @copyright Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md * @date 2020 * * @file SettingConfig.hpp - * @brief A tool for collecting settings, including from files and the command - * line. + * @brief A tool for collecting settings, including from files and the command line. * @note Status: DEPRECATED! */ #ifndef EMP_SETTING_CONFIG_H #define EMP_SETTING_CONFIG_H +#include #include #include #include -#include #include "../base/Ptr.hpp" #include "../base/vector.hpp" @@ -28,600 +26,553 @@ namespace emp { -/// Class to take a set of value for each "setting" and then step through all -/// combinations of those values for a factorial analysis. +/// Class to take a set of value for each "setting" and then step through all combinations of +/// those values for a factorial analysis. class SettingConfig { -private: - /// Base class to describe information about a single setting. - struct SettingBase { - std::string name; ///< Name for this setting - std::string desc; ///< Description of setting - char flag; ///< Command-line flag ('\0' for none) - std::string option; ///< Command-line longer option. - std::string args_label; ///< Label for option arguments (used in --help) - - SettingBase(const std::string &_name, const std::string &_desc, - const char _flag, const std::string &_args_label) - : name(_name), desc(_desc), flag(_flag), - option(emp::to_string("--", _name)), args_label(_args_label) {} - virtual ~SettingBase() {} - - virtual emp::Ptr Clone() const = 0; - - virtual size_t GetSize() const = 0; ///< How many values are available? - virtual std::string - AsString() const = 0; ///< All values, as a single string. - virtual std::string - AsString(size_t) const = 0; ///< A specified value as a string. - virtual bool FromString( - const std::string_view &) = 0; ///< Convert string to set of settings. - virtual bool SetValueID(size_t) { - return false; - } ///< Setup cur value in linked variable - virtual bool IsComboSetting() { - return false; - } ///< Do we have a combo setting? - virtual size_t GetID() const { - return (size_t)-1; - } ///< Combination ID for this setting. - - bool IsOptionMatch(const std::string &test_option) const { - return test_option == option; - } - bool IsFlagMatch(const char test_flag) const { return test_flag == flag; } - }; - - /// Full details about a single setting, including type information and - /// values. - template struct SettingInfo : public SettingBase { - T value; - emp::Ptr var_ptr = nullptr; - - SettingInfo( - const std::string &_name, ///< Unique name for this setting. - const std::string &_desc, ///< Description of this setting (for help) - const char _flag, ///< Single char flag for easy access (e.g., "-h") - const std::string &_arg, ///< Label for option argument (for help) - emp::Ptr _var = nullptr) ///< Pointer to variable to set (optional) - : SettingBase(_name, _desc, _flag, _arg), var_ptr(_var) { - ; - } + private: + /// Base class to describe information about a single setting. + struct SettingBase { + std::string name; ///< Name for this setting + std::string desc; ///< Description of setting + char flag; ///< Command-line flag ('\0' for none) + std::string option; ///< Command-line longer option. + std::string args_label; ///< Label for option arguments (used in --help) + + SettingBase(const std::string & _name, const std::string & _desc, + const char _flag, const std::string & _args_label) + : name(_name), desc(_desc), flag(_flag), option(emp::to_string("--",_name)) + , args_label(_args_label) { } + virtual ~SettingBase() { } + + virtual emp::Ptr Clone() const = 0; + + virtual size_t GetSize() const = 0; ///< How many values are available? + virtual std::string AsString() const = 0; ///< All values, as a single string. + virtual std::string AsString(size_t) const = 0; ///< A specified value as a string. + virtual bool FromString(const std::string_view &) = 0; ///< Convert string to set of settings. + virtual bool SetValueID(size_t) { return false; } ///< Setup cur value in linked variable + virtual bool IsComboSetting() { return false; } ///< Do we have a combo setting? + virtual size_t GetID() const { return (size_t)-1; } ///< Combination ID for this setting. + + bool IsOptionMatch(const std::string &test_option) const { return test_option == option; } + bool IsFlagMatch(const char test_flag) const { return test_flag == flag; } + }; + + /// Full details about a single setting, including type information and values. + template + struct SettingInfo : public SettingBase { + T value; + emp::Ptr var_ptr = nullptr; + + SettingInfo(const std::string & _name, ///< Unique name for this setting. + const std::string & _desc, ///< Description of this setting (for help) + const char _flag, ///< Single char flag for easy access (e.g., "-h") + const std::string & _arg, ///< Label for option argument (for help) + emp::Ptr _var=nullptr) ///< Pointer to variable to set (optional) + : SettingBase(_name, _desc, _flag, _arg), var_ptr(_var) { } + + ~SettingInfo(){if(var_ptr){var_ptr.Delete();}} + + emp::Ptr Clone() const override { + emp::Ptr new_var_ptr = nullptr; + if (var_ptr) { + new_var_ptr = NewPtr(*var_ptr); + } + emp::Ptr> setting_info_ptr = emp::NewPtr>(name, desc, flag, args_label, new_var_ptr); + setting_info_ptr->value = this->value; + return setting_info_ptr; + } - ~SettingInfo() { - if (var_ptr) { - var_ptr.Delete(); - } - } + size_t GetSize() const override { return 1; } + std::string AsString() const override { return emp::to_string(value); } + std::string AsString(size_t id) const override { + emp_assert(id == 0); + return emp::to_string(value); + } - emp::Ptr Clone() const override { - emp::Ptr new_var_ptr = nullptr; - if (var_ptr) { - new_var_ptr = NewPtr(*var_ptr); - } - emp::Ptr> setting_info_ptr = emp::NewPtr>( - name, desc, flag, args_label, new_var_ptr); - setting_info_ptr->value = this->value; - return setting_info_ptr; - } + bool FromString(const std::string_view & input) override { + value = emp::from_string(input); + // @CAO: Could do more tests to make sure whole string was used. + if (!var_ptr.IsNull()) *var_ptr = value; + return true; + } + }; + + /// Allow a single setting to have multiple values specified that should be stepped through. + template + struct ComboSettingInfo : public SettingBase { + emp::vector values; ///< Set of values to use for this setting. + emp::Ptr var_ptr = nullptr; ///< Pointer to variable to set as combos change. + size_t id; ///< Unique ID/position for this setting. + + ComboSettingInfo(const std::string & _name, ///< Unique name for this setting. + const std::string & _desc, ///< Description of this setting (for help) + const char _flag, ///< Char flag for easy access (e.g., "-h") + const std::string & _args, ///< Label for option arguments (for help) + emp::Ptr _var=nullptr) ///< Pointer to variable to set (optional) + : SettingBase(_name, _desc, _flag, _args), var_ptr(_var) { } + + ~ComboSettingInfo(){if(var_ptr){var_ptr.Delete();}} + + emp::Ptr Clone() const override { + emp::Ptr new_var_ptr = nullptr; + if (var_ptr) { + new_var_ptr = NewPtr(*var_ptr); + } + auto csi_ptr = emp::NewPtr>(name, desc, flag, args_label, new_var_ptr); + csi_ptr->values = this->values; + csi_ptr->id = this->id; + return csi_ptr; + } - size_t GetSize() const override { return 1; } - std::string AsString() const override { return emp::to_string(value); } - std::string AsString(size_t id) const override { - emp_assert(id == 0); - return emp::to_string(value); - } + size_t GetSize() const override { return values.size(); } + std::string AsString() const override { + std::stringstream ss; + for (size_t i=0; i < values.size(); i++) { + if (i) ss << ','; + ss << values[i]; + } + return ss.str(); + } - bool FromString(const std::string_view &input) override { - value = emp::from_string(input); - // @CAO: Could do more tests to make sure whole string was used. - if (!var_ptr.IsNull()) - *var_ptr = value; - return true; - } - }; - - /// Allow a single setting to have multiple values specified that should be - /// stepped through. - template struct ComboSettingInfo : public SettingBase { - emp::vector values; ///< Set of values to use for this setting. - emp::Ptr var_ptr = - nullptr; ///< Pointer to variable to set as combos change. - size_t id; ///< Unique ID/position for this setting. - - ComboSettingInfo( - const std::string &_name, ///< Unique name for this setting. - const std::string &_desc, ///< Description of this setting (for help) - const char _flag, ///< Char flag for easy access (e.g., "-h") - const std::string &_args, ///< Label for option arguments (for help) - emp::Ptr _var = nullptr) ///< Pointer to variable to set (optional) - : SettingBase(_name, _desc, _flag, _args), var_ptr(_var) { - ; - } + std::string AsString(size_t id) const override { + std::stringstream ss; + ss << values[id]; + return ss.str(); + } - ~ComboSettingInfo() { - if (var_ptr) { - var_ptr.Delete(); - } - } + bool FromString(const std::string_view & input) override { + // Divide up inputs into comma-separated units. + auto slices = emp::slice(input, ','); + + // Clear out the values to set, one at a time (in most cases, aleady clear) + values.resize(0); + + // Process each slice into one or more values. + for (auto & cur_str : slices) { + // If we are working with an arithmetic type, check if this is a range. + if constexpr (std::is_arithmetic::value) { + auto r_slices = emp::slice(cur_str, ':'); + if (r_slices.size() > 3) return false; // Error! Too many slices!!! + T start = emp::from_string( r_slices[0] ); + T end = emp::from_string( r_slices.back() ); // Same as start if one value. + T step = (r_slices.size() == 3) ? emp::from_string( r_slices[1] ) : 1; + while (start <= end) { + values.push_back(start); + start += step; + } + } + + // Otherwise do a direct conversion. + else { + values.push_back( emp::from_string(cur_str) ); + } + } + + if (!var_ptr.IsNull() && values.size()) *var_ptr = values[0]; + return values.size(); // Must result in at least one value. + } - emp::Ptr Clone() const override { - emp::Ptr new_var_ptr = nullptr; - if (var_ptr) { - new_var_ptr = NewPtr(*var_ptr); - } - auto csi_ptr = emp::NewPtr>(name, desc, flag, - args_label, new_var_ptr); - csi_ptr->values = this->values; - csi_ptr->id = this->id; - return csi_ptr; + bool SetValueID(size_t id) override { if (var_ptr) *var_ptr = values[id]; return true; } + bool IsComboSetting() override { return true; } + size_t GetID() const override { return id; } + }; + + /// A setting that is just a flag with an action function to run if it's called. + struct ActionFlag { + std::string name; ///< Name for this flag + std::string desc; ///< Description of flag + char flag; ///< Command-line flag ('\0' for none) + std::function fun; ///< Function to be called if flag is set. + }; + + std::string exe_name = ""; + + std::map> setting_map; ///< All settings by name + std::map action_map; ///< Action flags + + emp::vector> combo_settings; ///< Multi-value settings (in order) + emp::vector cur_combo; ///< Which combo settings are we currently using? + size_t combo_id = 0; ///< Unique value indicating which combination we are on. + + emp::vector unused_args; // Arguments that we were unable to process. + std::string errors; + + public: + SettingConfig() = default; + + SettingConfig(const SettingConfig &other) { + combo_settings.resize(other.combo_settings.size()); + for (const auto &entry : other.setting_map) { + setting_map[entry.first] = other.setting_map.at(entry.first)->Clone(); + } + for (size_t i = 0; i < combo_settings.size(); i++) { + combo_settings[i] = setting_map[other.combo_settings[i]->name]; + } + + action_map = other.action_map; + cur_combo = other.cur_combo; + combo_id = other.combo_id; + unused_args = other.unused_args; + errors = other.errors; + exe_name = other.exe_name; } - size_t GetSize() const override { return values.size(); } - std::string AsString() const override { - std::stringstream ss; - for (size_t i = 0; i < values.size(); i++) { - if (i) - ss << ','; - ss << values[i]; - } - return ss.str(); + ~SettingConfig() { + for (auto [name,ptr] : setting_map) ptr.Delete(); } - std::string AsString(size_t id) const override { - std::stringstream ss; - ss << values[id]; - return ss.str(); + const std::string & GetExeName() const { return exe_name; } + size_t GetComboID() const { return combo_id; } + const emp::vector & GetUnusedArgs() const { return unused_args; } + const std::string & GetErrors() const { return errors; } + + bool HasUnusedArgs() const { return unused_args.size(); } + bool HasErrors() const { return errors.size(); } + + /// Retrieves all of the setting names in config and places into std::vector + std::vector GetSettingMapNames() const { + std::vector result; + for (const auto &p : setting_map) { + result.push_back(p.first); + } + return result; } - bool FromString(const std::string_view &input) override { - // Divide up inputs into comma-separated units. - auto slices = emp::slice(input, ','); - - // Clear out the values to set, one at a time (in most cases, aleady - // clear) - values.resize(0); - - // Process each slice into one or more values. - for (auto &cur_str : slices) { - // If we are working with an arithmetic type, check if this is a range. - if constexpr (std::is_arithmetic::value) { - auto r_slices = emp::slice(cur_str, ':'); - if (r_slices.size() > 3) - return false; // Error! Too many slices!!! - T start = emp::from_string(r_slices[0]); - T end = emp::from_string( - r_slices.back()); // Same as start if one value. - T step = - (r_slices.size() == 3) ? emp::from_string(r_slices[1]) : 1; - while (start <= end) { - values.push_back(start); - start += step; - } + /// Retrieves all of the setting names in config and places into std::vector + std::vector> GetSettingMapBase() const { + std::vector> result; + for (const auto &p : setting_map) { + result.push_back(p.second); } + return result; + } - // Otherwise do a direct conversion. - else { - values.push_back(emp::from_string(cur_str)); + /// Get the current value of a specified setting. + template + const T & GetValue(const std::string & name) const { + emp_assert(emp::Has(setting_map, name), name); + emp::Ptr base_ptr = setting_map.find(name)->second; + + // If we have a combo setting, determine the current value. + if (base_ptr->IsComboSetting()) { + emp::Ptr> ptr = base_ptr.Cast>(); + size_t id = cur_combo[ptr->id]; + return ptr->values[id]; } - } - if (!var_ptr.IsNull() && values.size()) - *var_ptr = values[0]; - return values.size(); // Must result in at least one value. + // Otherwise we have a regular setting. + return base_ptr.Cast>()->value; } - bool SetValueID(size_t id) override { - if (var_ptr) - *var_ptr = values[id]; - return true; + /// Scan through all values and return the maximum. + template + T MaxValue(const std::string & name) const { + emp_assert(emp::Has(setting_map, name), name); + emp::Ptr base_ptr = setting_map.find(name)->second; + + // If we have a combo setting, determine the current value. + if (base_ptr->IsComboSetting()) { + emp::Ptr> ptr = base_ptr.Cast>(); + return emp::FindMax(ptr->values); + } + + // Otherwise we have a regular setting with just one value. + return base_ptr.Cast>()->value; } - bool IsComboSetting() override { return true; } - size_t GetID() const override { return id; } - }; - - /// A setting that is just a flag with an action function to run if it's - /// called. - struct ActionFlag { - std::string name; ///< Name for this flag - std::string desc; ///< Description of flag - char flag; ///< Command-line flag ('\0' for none) - std::function fun; ///< Function to be called if flag is set. - }; - - std::string exe_name = ""; - - std::map> - setting_map; ///< All settings by name - std::map action_map; ///< Action flags - - emp::vector> - combo_settings; ///< Multi-value settings (in order) - emp::vector - cur_combo; ///< Which combo settings are we currently using? - size_t combo_id = 0; ///< Unique value indicating which combination we are on. - - emp::vector - unused_args; // Arguments that we were unable to process. - std::string errors; - -public: - SettingConfig() = default; - - SettingConfig(const SettingConfig &other) { - combo_settings.resize(other.combo_settings.size()); - for (const auto &entry : other.setting_map) { - setting_map[entry.first] = other.setting_map.at(entry.first)->Clone(); + + /// Add a new setting of a specified type. Returns the (initially empty) vector of values + /// to allow easy setting. + /// Example: + /// config.AddSetting("num_runs") = 200; + + template + T & AddSetting(const std::string & name, + const std::string & desc, + const char option_flag, + T & var, + const std::string & args_label="Value") + { + emp_assert(!emp::Has(setting_map, name)); + auto new_ptr = emp::NewPtr>(name, desc, option_flag, args_label, &var); + setting_map[name] = new_ptr; + return new_ptr->value; } - for (size_t i = 0; i < combo_settings.size(); i++) { - combo_settings[i] = setting_map[other.combo_settings[i]->name]; + + /// Add a new setting not linked to a variable + + template + T &AddSetting(const std::string &name, + const std::string &desc = "", + const char option_flag = '\0') { + emp_assert(!emp::Has(setting_map, name)); + auto new_ptr = emp::NewPtr>(name, desc, option_flag, "Value"); + setting_map[name] = new_ptr; + return new_ptr->value; } - action_map = other.action_map; - cur_combo = other.cur_combo; - combo_id = other.combo_id; - unused_args = other.unused_args; - errors = other.errors; - exe_name = other.exe_name; - } - - ~SettingConfig() { - for (auto [name, ptr] : setting_map) - ptr.Delete(); - } - - const std::string &GetExeName() const { return exe_name; } - size_t GetComboID() const { return combo_id; } - const emp::vector &GetUnusedArgs() const { return unused_args; } - const std::string &GetErrors() const { return errors; } - - bool HasUnusedArgs() const { return unused_args.size(); } - bool HasErrors() const { return errors.size(); } - - /// Retrieves all of the setting names in config and places into std::vector - std::vector GetSettingMapNames() const { - std::vector result; - for (const auto &p : setting_map) { - result.push_back(p.first); + /// Add a new setting of a specified type. Returns the (initially empty) vector of values + /// to allow easy setting. + /// Example: + /// config.AddComboSetting("pop_size") = { 100,200,400,800 }; + + template + emp::vector & AddComboSetting(const std::string & name, + const std::string & desc="", + const char option_flag='\0') { + emp_assert(!emp::Has(setting_map, name)); + auto new_ptr = emp::NewPtr>(name, desc, option_flag, "Values..."); + new_ptr->id = combo_settings.size(); + combo_settings.push_back(new_ptr); + setting_map[name] = new_ptr; + cur_combo.push_back(0); + return new_ptr->values; } - return result; - } - - /// Retrieves all of the setting names in config and places into std::vector - std::vector> GetSettingMapBase() const { - std::vector> result; - for (const auto &p : setting_map) { - result.push_back(p.second); + + /// A setting can also be linked to a value that is kept up-to-date. + template + emp::vector & AddComboSetting(const std::string & name, + const std::string & desc, + const char option_flag, + T & var, + const std::string & args_label="Values...") + { + emp_assert(!emp::Has(setting_map, name)); + auto new_ptr = emp::NewPtr>(name, desc, option_flag, args_label, &var); + new_ptr->id = combo_settings.size(); + combo_settings.push_back(new_ptr); + setting_map[name] = new_ptr; + cur_combo.push_back(0); + return new_ptr->values; } - return result; - } - - /// Get the current value of a specified setting. - template const T &GetValue(const std::string &name) const { - emp_assert(emp::Has(setting_map, name), name); - emp::Ptr base_ptr = setting_map.find(name)->second; - - // If we have a combo setting, determine the current value. - if (base_ptr->IsComboSetting()) { - emp::Ptr> ptr = base_ptr.Cast>(); - size_t id = cur_combo[ptr->id]; - return ptr->values[id]; + + void AddAction(const std::string & name, + const std::string & desc, + const char flag, + std::function fun) + { + std::string name_option = emp::to_string("--", name); + std::string flag_option = emp::to_string("-", flag); + emp_assert(!emp::Has(action_map, name_option)); + emp_assert(!emp::Has(action_map, flag_option)); + action_map[name_option] = ActionFlag{ name, desc, flag, fun }; + action_map[flag_option] = ActionFlag{ name, desc, flag, fun }; } - // Otherwise we have a regular setting. - return base_ptr.Cast>()->value; - } + /// Access ALL values for a specified setting, to be modified freely. + template + emp::vector & ComboValues(const std::string & name) { + emp_assert(emp::Has(setting_map, name), name); + emp_assert(setting_map[name]->IsComboSetting()); + return setting_map[name].DynamicCast>()->values; + } - /// Scan through all values and return the maximum. - template T MaxValue(const std::string &name) const { - emp_assert(emp::Has(setting_map, name), name); - emp::Ptr base_ptr = setting_map.find(name)->second; + /// Start over stepping through all combinations of parameter values. + void ResetCombos() { + // Setup as base combo. + for (size_t & x : cur_combo) x = 0; + combo_id = 0; - // If we have a combo setting, determine the current value. - if (base_ptr->IsComboSetting()) { - emp::Ptr> ptr = base_ptr.Cast>(); - return emp::FindMax(ptr->values); + // Setup all linked values. + for (auto x : combo_settings) x->SetValueID(0); } - // Otherwise we have a regular setting with just one value. - return base_ptr.Cast>()->value; - } - - /// Add a new setting of a specified type. Returns the (initially empty) - /// vector of values to allow easy setting. Example: - /// config.AddSetting("num_runs") = 200; - - template - T &AddSetting(const std::string &name, const std::string &desc, - const char option_flag, T &var, - const std::string &args_label = "Value") { - emp_assert(!emp::Has(setting_map, name)); - auto new_ptr = - emp::NewPtr>(name, desc, option_flag, args_label, &var); - setting_map[name] = new_ptr; - return new_ptr->value; - } - - /// Add a new setting not linked to a variable - - template - T &AddSetting(const std::string &name, const std::string &desc = "", - const char option_flag = '\0') { - emp_assert(!emp::Has(setting_map, name)); - auto new_ptr = - emp::NewPtr>(name, desc, option_flag, "Value"); - setting_map[name] = new_ptr; - return new_ptr->value; - } - - /// Add a new setting of a specified type. Returns the (initially empty) - /// vector of values to allow easy setting. Example: - /// config.AddComboSetting("pop_size") = { 100,200,400,800 }; - - template - emp::vector &AddComboSetting(const std::string &name, - const std::string &desc = "", - const char option_flag = '\0') { - emp_assert(!emp::Has(setting_map, name)); - auto new_ptr = - emp::NewPtr>(name, desc, option_flag, "Values..."); - new_ptr->id = combo_settings.size(); - combo_settings.push_back(new_ptr); - setting_map[name] = new_ptr; - cur_combo.push_back(0); - return new_ptr->values; - } - - /// A setting can also be linked to a value that is kept up-to-date. - template - emp::vector &AddComboSetting(const std::string &name, - const std::string &desc, - const char option_flag, T &var, - const std::string &args_label = "Values...") { - emp_assert(!emp::Has(setting_map, name)); - auto new_ptr = emp::NewPtr>(name, desc, option_flag, - args_label, &var); - new_ptr->id = combo_settings.size(); - combo_settings.push_back(new_ptr); - setting_map[name] = new_ptr; - cur_combo.push_back(0); - return new_ptr->values; - } - - void AddAction(const std::string &name, const std::string &desc, - const char flag, std::function fun) { - std::string name_option = emp::to_string("--", name); - std::string flag_option = emp::to_string("-", flag); - emp_assert(!emp::Has(action_map, name_option)); - emp_assert(!emp::Has(action_map, flag_option)); - action_map[name_option] = ActionFlag{name, desc, flag, fun}; - action_map[flag_option] = ActionFlag{name, desc, flag, fun}; - } - - /// Access ALL values for a specified setting, to be modified freely. - template emp::vector &ComboValues(const std::string &name) { - emp_assert(emp::Has(setting_map, name), name); - emp_assert(setting_map[name]->IsComboSetting()); - return setting_map[name].DynamicCast>()->values; - } - - /// Start over stepping through all combinations of parameter values. - void ResetCombos() { - // Setup as base combo. - for (size_t &x : cur_combo) - x = 0; - combo_id = 0; - - // Setup all linked values. - for (auto x : combo_settings) - x->SetValueID(0); - } - - /// Add a single new value to the specified setting. - template void AddComboValue(const std::string &name, T &&val) { - emp_assert(emp::Has(setting_map, name), name); - emp_assert(setting_map[name]->IsComboSetting()); - auto ptr = setting_map[name].DynamicCast>(); - ptr->values.emplace_back(std::forward(val)); - } - - /// Set all values for the specified setting. - template - void SetComboValues(const std::string &name, T1 &&val1, Ts &&...vals) { - emp_assert(emp::Has(setting_map, name)); - auto ptr = setting_map[name].DynamicCast>(); - emp::Append(ptr->values, std::forward(val1), std::forward(vals)...); - } - - /// Determine how many unique combinations there currently are. - size_t CountCombos() { - size_t result = 1; - for (auto ptr : combo_settings) - result *= ptr->GetSize(); - return result; - } - - /// Set the next combination of settings to be active. Return true if - /// successful or false if we ran through all combinations and reset. - bool NextCombo() { - combo_id++; - for (size_t i = 0; i < cur_combo.size(); i++) { - cur_combo[i]++; - - // Check if this new combo is valid. - if (cur_combo[i] < combo_settings[i]->GetSize()) { - combo_settings[i]->SetValueID( - cur_combo[i]); // Set value in linked variable. - return true; - } + /// Add a single new value to the specified setting. + template + void AddComboValue(const std::string & name, T && val) { + emp_assert(emp::Has(setting_map, name), name); + emp_assert(setting_map[name]->IsComboSetting()); + auto ptr = setting_map[name].DynamicCast>(); + ptr->values.emplace_back(std::forward(val)); + } - // Since it's not, prepare to move on to the next one. - cur_combo[i] = 0; - combo_settings[i]->SetValueID(0); + /// Set all values for the specified setting. + template + void SetComboValues(const std::string & name, T1 && val1, Ts &&... vals) { + emp_assert(emp::Has(setting_map, name)); + auto ptr = setting_map[name].DynamicCast>(); + emp::Append(ptr->values, std::forward(val1), std::forward(vals)...); } - // No valid combo found. - combo_id = 0; - return false; - } - - /// Get the set of headers used for the CSV file. - std::string GetSettingHeaders(const std::string &separator = ",") { - std::string out_str; - for (auto [name, ptr] : setting_map) { - if (out_str.size()) - out_str += separator; - out_str += ptr->name; + /// Determine how many unique combinations there currently are. + size_t CountCombos() { + size_t result = 1; + for (auto ptr : combo_settings) result *= ptr->GetSize(); + return result; } - return out_str; - } - - /// Convert all of the current values into a comma-separated string. - std::string CurSettings(const std::string &separator = ",") const { - std::string out_str; - for (auto [name, ptr] : setting_map) { - if (ptr) { - if (out_str.size()) - out_str += separator; - out_str += ptr->IsComboSetting() - ? ptr->AsString(cur_combo[ptr->GetID()]) - : ptr->AsString(); + + /// Set the next combination of settings to be active. Return true if successful + /// or false if we ran through all combinations and reset. + bool NextCombo() { + combo_id++; + for (size_t i = 0; i < cur_combo.size(); i++) { + cur_combo[i]++; + + // Check if this new combo is valid. + if (cur_combo[i] < combo_settings[i]->GetSize()) { + combo_settings[i]->SetValueID( cur_combo[i] ); // Set value in linked variable. + return true; + } + + // Since it's not, prepare to move on to the next one. + cur_combo[i] = 0; + combo_settings[i]->SetValueID(0); } + + // No valid combo found. + combo_id = 0; + return false; } - return out_str; - } - - /// Get the set of headers used for the CSV file. - std::string GetComboHeaders(const std::string &separator = ",") { - std::string out_string; - for (size_t i = 0; i < combo_settings.size(); i++) { - if (i) - out_string += separator; - out_string += combo_settings[i]->name; + + /// Get the set of headers used for the CSV file. + std::string GetSettingHeaders(const std::string & separator=",") { + std::string out_str; + for (auto [name,ptr] : setting_map) { + if (out_str.size()) out_str += separator; + out_str += ptr->name; + } + return out_str; } - return out_string; - } - - /// Convert all of the current values into a comma-separated string. - std::string - CurComboString(const std::string &separator = ",", - bool use_labels = false, ///< Print name with each value? - bool multi_only = false ///< Only print values that can change? - ) const { - std::string out_str; - for (size_t i = 0; i < cur_combo.size(); i++) { - if (multi_only && combo_settings[i]->GetSize() == 1) - continue; - if (out_str.size()) - out_str += separator; - if (use_labels) - out_str += emp::to_string(combo_settings[i]->name, '='); - out_str += combo_settings[i]->AsString(cur_combo[i]); + + /// Convert all of the current values into a comma-separated string. + std::string CurSettings(const std::string & separator=",") const { + std::string out_str; + for (auto [name,ptr] : setting_map) { + if (ptr) { + if (out_str.size()) out_str += separator; + out_str += ptr->IsComboSetting() ? ptr->AsString(cur_combo[ptr->GetID()]) : ptr->AsString(); + } + } + return out_str; } - return out_str; - } - - /// Scan through all settings for a match option and return ID. - emp::Ptr FindOptionMatch(const std::string &option_name) { - for (const auto &[name, ptr] : setting_map) { - if (ptr->IsOptionMatch(option_name)) - return ptr; + + /// Get the set of headers used for the CSV file. + std::string GetComboHeaders(const std::string & separator=",") { + std::string out_string; + for (size_t i = 0; i < combo_settings.size(); i++) { + if (i) out_string += separator; + out_string += combo_settings[i]->name; + } + return out_string; } - return nullptr; - } - - /// Scan through all settings for a match option and return ID. - emp::Ptr FindFlagMatch(const char symbol) { - for (const auto &[name, ptr] : setting_map) { - if (ptr->IsFlagMatch(symbol)) - return ptr; + + /// Convert all of the current values into a comma-separated string. + std::string CurComboString(const std::string & separator=",", + bool use_labels=false, ///< Print name with each value? + bool multi_only=false ///< Only print values that can change? + ) const { + std::string out_str; + for (size_t i = 0; i < cur_combo.size(); i++) { + if (multi_only && combo_settings[i]->GetSize() == 1) continue; + if (out_str.size()) out_str += separator; + if (use_labels) out_str += emp::to_string(combo_settings[i]->name, '='); + out_str += combo_settings[i]->AsString(cur_combo[i]); + } + return out_str; } - return nullptr; - } - - /// Take an input set of config options, process them, and track set of - /// unprocessed ones. - bool ProcessOptions(const emp::vector &args) { - exe_name = args[0]; - - for (size_t i = 1; i < args.size(); i++) { - const std::string &cur_arg = args[i]; - if (cur_arg.size() < 2 || cur_arg[0] != '-') { - unused_args.push_back(cur_arg); - continue; // If isn't an option, continue. - } - // See if this is a fully spelled-out option. - auto setting_ptr = FindOptionMatch(cur_arg); - if (!setting_ptr.IsNull()) { - if (++i >= args.size()) { - errors += "ERROR: Must provide args for option '--"; - errors += setting_ptr->name; - errors += "' to use!\n"; - std::cerr << errors; - return false; + /// Scan through all settings for a match option and return ID. + emp::Ptr FindOptionMatch(const std::string & option_name) { + for (const auto & [name, ptr] : setting_map) { + if (ptr->IsOptionMatch(option_name)) return ptr; } - setting_ptr->FromString(args[i]); - // @CAO: Should make sure string translated correctly. - } + return nullptr; + } - // See if we have a flag option. - setting_ptr = FindFlagMatch(cur_arg[1]); - if (!setting_ptr.IsNull()) { - // Check if the flag is followed by the values without whitespace. - if (cur_arg.size() > 2) { - setting_ptr->FromString(emp::view_string(cur_arg, 2)); - } else if (++i >= args.size()) { - std::cout << "ERROR: Must provide args to use!\n"; - return false; - } else { + /// Scan through all settings for a match option and return ID. + emp::Ptr FindFlagMatch(const char symbol) { + for (const auto & [name, ptr] : setting_map) { + if (ptr->IsFlagMatch(symbol)) return ptr; + } + return nullptr; + } + + /// Take an input set of config options, process them, and track set of unprocessed ones. + bool ProcessOptions(const emp::vector & args) { + exe_name = args[0]; + + for (size_t i = 1; i < args.size(); i++) { + const std::string & cur_arg = args[i]; + if (cur_arg.size() < 2 || cur_arg[0] != '-') { + unused_args.push_back(cur_arg); + continue; // If isn't an option, continue. + } + + // See if this is a fully spelled-out option. + auto setting_ptr = FindOptionMatch(cur_arg); + if (!setting_ptr.IsNull()) { + if (++i >= args.size()) { + errors += "ERROR: Must provide args for option '--"; + errors += setting_ptr->name; + errors += "' to use!\n"; + std::cerr << errors; + return false; + } setting_ptr->FromString(args[i]); + // @CAO: Should make sure string translated correctly. } - } - // Or see of this is a flag trigger. - else if (Has(action_map, cur_arg)) { - action_map[cur_arg].fun(); - } + // See if we have a flag option. + setting_ptr = FindFlagMatch(cur_arg[1]); + if (!setting_ptr.IsNull()) { + // Check if the flag is followed by the values without whitespace. + if (cur_arg.size() > 2) { + setting_ptr->FromString( emp::view_string(cur_arg,2) ); + } + else if (++i >= args.size()) { + std::cout << "ERROR: Must provide args to use!\n"; + return false; + } + else { + setting_ptr->FromString(args[i]); + } + } - // Otherwise this argument will go unused; send it back. - else - unused_args.push_back(cur_arg); - } + // Or see of this is a flag trigger. + else if (Has(action_map, cur_arg)) { + action_map[cur_arg].fun(); + } - return true; - } - - bool ProcessOptions(int argc, char *argv[]) { - return ProcessOptions(emp::cl::args_to_strings(argc, argv)); - } - - template void PrintHelp(const Ts &...examples) const { - std::cout << "Format: " << exe_name << " [OPTIONS...]\n" - << "\nSetting Options:\n"; - for (auto [name, ptr] : setting_map) { - std::string spacing(emp::Max(1, 12 - (int)ptr->args_label.size()), ' '); - std::cout << " -" << ptr->flag << " [" << ptr->args_label << "]" - << spacing << ": " << ptr->desc << " (--" << name << ") [" - << ptr->AsString() << "]\n"; - } + // Otherwise this argument will go unused; send it back. + else unused_args.push_back(cur_arg); + } - std::cout << "\nAction Options:\n"; - for (auto [name, action] : action_map) { - if (name.size() == 2) - continue; // Skip flag entries. - std::cout << " -" << action.flag << " : " << action.desc << " (" << name - << ")\n"; + return true; } - if constexpr (sizeof...(examples) > 0) { - std::cout << "\nExample: " << emp::to_string(examples...) << std::endl; + bool ProcessOptions(int argc, char* argv[]) { + return ProcessOptions(emp::cl::args_to_strings(argc, argv)); } - std::cout.flush(); - } -}; // namespace emp -} // namespace emp + template + void PrintHelp(const Ts &... examples) const { + + std::cout << "Format: " << exe_name << " [OPTIONS...]\n" + << "\nSetting Options:\n"; + for (auto [name, ptr] : setting_map) { + std::string spacing(emp::Max(1, 12 - (int) ptr->args_label.size()), ' '); + std::cout << " -" << ptr->flag << " [" << ptr->args_label << "]" << spacing << ": " + << ptr->desc << " (--" << name << ") [" + << ptr->AsString() << "]\n"; + } + + std::cout << "\nAction Options:\n"; + for (auto [name, action] : action_map) { + if (name.size() == 2) continue; // Skip flag entries. + std::cout << " -" << action.flag << " : " + << action.desc << " (" << name << ")\n"; + } + + if constexpr (sizeof...(examples) > 0) { + std::cout << "\nExample: " << emp::to_string(examples...) << std::endl; + } + + std::cout.flush(); + } +}; + +} #endif From 239511dc039c4a01d3dba4c4f2e80f8d09df4b27 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 18 Mar 2021 16:24:45 -0400 Subject: [PATCH 15/19] another attempted formatting fix --- include/emp/config/SettingConfig.hpp | 99 ++++++++++++++-------------- 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/include/emp/config/SettingConfig.hpp b/include/emp/config/SettingConfig.hpp index 0d17e60e6f..7afdcfcb7c 100644 --- a/include/emp/config/SettingConfig.hpp +++ b/include/emp/config/SettingConfig.hpp @@ -26,11 +26,11 @@ namespace emp { -/// Class to take a set of value for each "setting" and then step through all combinations of -/// those values for a factorial analysis. + /// Class to take a set of value for each "setting" and then step through all combinations of + /// those values for a factorial analysis. -class SettingConfig { - private: + class SettingConfig { + private: /// Base class to describe information about a single setting. struct SettingBase { std::string name; ///< Name for this setting @@ -40,22 +40,21 @@ class SettingConfig { std::string args_label; ///< Label for option arguments (used in --help) SettingBase(const std::string & _name, const std::string & _desc, - const char _flag, const std::string & _args_label) - : name(_name), desc(_desc), flag(_flag), option(emp::to_string("--",_name)) - , args_label(_args_label) { } + const char _flag, const std::string & _args_label) + : name(_name), desc(_desc), flag(_flag), option(emp::to_string("--",_name)) + , args_label(_args_label) { } virtual ~SettingBase() { } + virtual size_t GetSize() const = 0; ///< How many values are available? + virtual std::string AsString() const = 0; ///< All values, as a single string. + virtual std::string AsString(size_t) const = 0; ///< A specified value as a string. + virtual bool FromString(const std::string_view &) = 0; ///< Convert string to set of settings. + virtual bool SetValueID(size_t) {return false; } ///< Setup cur value in linked variable + virtual bool IsComboSetting() { return false; } ///< Do we have a combo setting? + virtual size_t GetID() const { return (size_t) -1; } ///< Combination ID for this setting. virtual emp::Ptr Clone() const = 0; - virtual size_t GetSize() const = 0; ///< How many values are available? - virtual std::string AsString() const = 0; ///< All values, as a single string. - virtual std::string AsString(size_t) const = 0; ///< A specified value as a string. - virtual bool FromString(const std::string_view &) = 0; ///< Convert string to set of settings. - virtual bool SetValueID(size_t) { return false; } ///< Setup cur value in linked variable - virtual bool IsComboSetting() { return false; } ///< Do we have a combo setting? - virtual size_t GetID() const { return (size_t)-1; } ///< Combination ID for this setting. - - bool IsOptionMatch(const std::string &test_option) const { return test_option == option; } + bool IsOptionMatch(const std::string & test_option) const { return test_option == option; } bool IsFlagMatch(const char test_flag) const { return test_flag == flag; } }; @@ -66,10 +65,10 @@ class SettingConfig { emp::Ptr var_ptr = nullptr; SettingInfo(const std::string & _name, ///< Unique name for this setting. - const std::string & _desc, ///< Description of this setting (for help) - const char _flag, ///< Single char flag for easy access (e.g., "-h") - const std::string & _arg, ///< Label for option argument (for help) - emp::Ptr _var=nullptr) ///< Pointer to variable to set (optional) + const std::string & _desc, ///< Description of this setting (for help) + const char _flag, ///< Single char flag for easy access (e.g., "-h") + const std::string & _arg, ///< Label for option argument (for help) + emp::Ptr _var=nullptr) ///< Pointer to variable to set (optional) : SettingBase(_name, _desc, _flag, _arg), var_ptr(_var) { } ~SettingInfo(){if(var_ptr){var_ptr.Delete();}} @@ -143,48 +142,48 @@ class SettingConfig { } bool FromString(const std::string_view & input) override { - // Divide up inputs into comma-separated units. - auto slices = emp::slice(input, ','); + // Divide up inputs into comma-separated units. + auto slices = emp::slice(input, ','); - // Clear out the values to set, one at a time (in most cases, aleady clear) - values.resize(0); + // Clear out the values to set, one at a time (in most cases, aleady clear) + values.resize(0); // Process each slice into one or more values. - for (auto & cur_str : slices) { - // If we are working with an arithmetic type, check if this is a range. - if constexpr (std::is_arithmetic::value) { - auto r_slices = emp::slice(cur_str, ':'); - if (r_slices.size() > 3) return false; // Error! Too many slices!!! - T start = emp::from_string( r_slices[0] ); - T end = emp::from_string( r_slices.back() ); // Same as start if one value. - T step = (r_slices.size() == 3) ? emp::from_string( r_slices[1] ) : 1; - while (start <= end) { - values.push_back(start); - start += step; - } + for (auto & cur_str : slices) { + // If we are working with an arithmetic type, check if this is a range. + if constexpr (std::is_arithmetic::value) { + auto r_slices = emp::slice(cur_str, ':'); + if (r_slices.size() > 3) return false; // Error! Too many slices!!! + T start = emp::from_string( r_slices[0] ); + T end = emp::from_string( r_slices.back() ); // Same as start if one value. + T step = (r_slices.size() == 3) ? emp::from_string( r_slices[1] ) : 1; + while (start <= end) { + values.push_back(start); + start += step; } + } - // Otherwise do a direct conversion. - else { - values.push_back( emp::from_string(cur_str) ); - } + // Otherwise do a direct conversion. + else { + values.push_back( emp::from_string(cur_str) ); } + } - if (!var_ptr.IsNull() && values.size()) *var_ptr = values[0]; - return values.size(); // Must result in at least one value. - } + if (!var_ptr.IsNull() && values.size()) *var_ptr = values[0]; + return values.size(); // Must result in at least one value. + } - bool SetValueID(size_t id) override { if (var_ptr) *var_ptr = values[id]; return true; } - bool IsComboSetting() override { return true; } - size_t GetID() const override { return id; } + bool SetValueID(size_t id) override { if (var_ptr) *var_ptr = values[id]; return true; } + bool IsComboSetting() override { return true; } + size_t GetID() const override { return id; } }; /// A setting that is just a flag with an action function to run if it's called. struct ActionFlag { - std::string name; ///< Name for this flag - std::string desc; ///< Description of flag - char flag; ///< Command-line flag ('\0' for none) - std::function fun; ///< Function to be called if flag is set. + std::string name; ///< Name for this flag + std::string desc; ///< Description of flag + char flag; ///< Command-line flag ('\0' for none) + std::function fun; ///< Function to be called if flag is set. }; std::string exe_name = ""; From df03da25cb95c443cd47f0ab2bfb7dc18f9206d3 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 18 Mar 2021 17:01:03 -0400 Subject: [PATCH 16/19] maybe actually fix formatting --- include/emp/config/SettingConfig.hpp | 384 +++++++++++++-------------- 1 file changed, 192 insertions(+), 192 deletions(-) diff --git a/include/emp/config/SettingConfig.hpp b/include/emp/config/SettingConfig.hpp index 7afdcfcb7c..d4222c8791 100644 --- a/include/emp/config/SettingConfig.hpp +++ b/include/emp/config/SettingConfig.hpp @@ -105,12 +105,12 @@ namespace emp { emp::Ptr var_ptr = nullptr; ///< Pointer to variable to set as combos change. size_t id; ///< Unique ID/position for this setting. - ComboSettingInfo(const std::string & _name, ///< Unique name for this setting. - const std::string & _desc, ///< Description of this setting (for help) - const char _flag, ///< Char flag for easy access (e.g., "-h") - const std::string & _args, ///< Label for option arguments (for help) - emp::Ptr _var=nullptr) ///< Pointer to variable to set (optional) - : SettingBase(_name, _desc, _flag, _args), var_ptr(_var) { } + ComboSettingInfo(const std::string & _name, ///< Unique name for this setting. + const std::string & _desc, ///< Description of this setting (for help) + const char _flag, ///< Char flag for easy access (e.g., "-h") + const std::string & _args, ///< Label for option arguments (for help) + emp::Ptr _var=nullptr) ///< Pointer to variable to set (optional) + : SettingBase(_name, _desc, _flag, _args), var_ptr(_var) { } ~ComboSettingInfo(){if(var_ptr){var_ptr.Delete();}} @@ -125,52 +125,52 @@ namespace emp { return csi_ptr; } - size_t GetSize() const override { return values.size(); } - std::string AsString() const override { - std::stringstream ss; - for (size_t i=0; i < values.size(); i++) { - if (i) ss << ','; - ss << values[i]; - } - return ss.str(); - } + size_t GetSize() const override { return values.size(); } + std::string AsString() const override { + std::stringstream ss; + for (size_t i=0; i < values.size(); i++) { + if (i) ss << ','; + ss << values[i]; + } + return ss.str(); + } - std::string AsString(size_t id) const override { - std::stringstream ss; - ss << values[id]; - return ss.str(); - } + std::string AsString(size_t id) const override { + std::stringstream ss; + ss << values[id]; + return ss.str(); + } - bool FromString(const std::string_view & input) override { - // Divide up inputs into comma-separated units. - auto slices = emp::slice(input, ','); - - // Clear out the values to set, one at a time (in most cases, aleady clear) - values.resize(0); - - // Process each slice into one or more values. - for (auto & cur_str : slices) { - // If we are working with an arithmetic type, check if this is a range. - if constexpr (std::is_arithmetic::value) { - auto r_slices = emp::slice(cur_str, ':'); - if (r_slices.size() > 3) return false; // Error! Too many slices!!! - T start = emp::from_string( r_slices[0] ); - T end = emp::from_string( r_slices.back() ); // Same as start if one value. - T step = (r_slices.size() == 3) ? emp::from_string( r_slices[1] ) : 1; - while (start <= end) { - values.push_back(start); - start += step; - } + bool FromString(const std::string_view & input) override { + // Divide up inputs into comma-separated units. + auto slices = emp::slice(input, ','); + + // Clear out the values to set, one at a time (in most cases, aleady clear) + values.resize(0); + + // Process each slice into one or more values. + for (auto & cur_str : slices) { + // If we are working with an arithmetic type, check if this is a range. + if constexpr (std::is_arithmetic::value) { + auto r_slices = emp::slice(cur_str, ':'); + if (r_slices.size() > 3) return false; // Error! Too many slices!!! + T start = emp::from_string( r_slices[0] ); + T end = emp::from_string( r_slices.back() ); // Same as start if one value. + T step = (r_slices.size() == 3) ? emp::from_string( r_slices[1] ) : 1; + while (start <= end) { + values.push_back(start); + start += step; } + } - // Otherwise do a direct conversion. - else { - values.push_back( emp::from_string(cur_str) ); - } + // Otherwise do a direct conversion. + else { + values.push_back( emp::from_string(cur_str) ); } + } - if (!var_ptr.IsNull() && values.size()) *var_ptr = values[0]; - return values.size(); // Must result in at least one value. + if (!var_ptr.IsNull() && values.size()) *var_ptr = values[0]; + return values.size(); // Must result in at least one value. } bool SetValueID(size_t id) override { if (var_ptr) *var_ptr = values[id]; return true; } @@ -198,28 +198,28 @@ namespace emp { emp::vector unused_args; // Arguments that we were unable to process. std::string errors; - public: + public: SettingConfig() = default; SettingConfig(const SettingConfig &other) { - combo_settings.resize(other.combo_settings.size()); - for (const auto &entry : other.setting_map) { - setting_map[entry.first] = other.setting_map.at(entry.first)->Clone(); - } - for (size_t i = 0; i < combo_settings.size(); i++) { - combo_settings[i] = setting_map[other.combo_settings[i]->name]; - } + combo_settings.resize(other.combo_settings.size()); + for (const auto &entry : other.setting_map) { + setting_map[entry.first] = other.setting_map.at(entry.first)->Clone(); + } + for (size_t i = 0; i < combo_settings.size(); i++) { + combo_settings[i] = setting_map[other.combo_settings[i]->name]; + } - action_map = other.action_map; - cur_combo = other.cur_combo; - combo_id = other.combo_id; - unused_args = other.unused_args; - errors = other.errors; - exe_name = other.exe_name; + action_map = other.action_map; + cur_combo = other.cur_combo; + combo_id = other.combo_id; + unused_args = other.unused_args; + errors = other.errors; + exe_name = other.exe_name; } ~SettingConfig() { - for (auto [name,ptr] : setting_map) ptr.Delete(); + for (auto [name,ptr] : setting_map) ptr.Delete(); } const std::string & GetExeName() const { return exe_name; } @@ -232,53 +232,53 @@ namespace emp { /// Retrieves all of the setting names in config and places into std::vector std::vector GetSettingMapNames() const { - std::vector result; - for (const auto &p : setting_map) { - result.push_back(p.first); - } - return result; + std::vector result; + for (const auto &p : setting_map) { + result.push_back(p.first); + } + return result; } /// Retrieves all of the setting names in config and places into std::vector std::vector> GetSettingMapBase() const { - std::vector> result; - for (const auto &p : setting_map) { - result.push_back(p.second); - } - return result; + std::vector> result; + for (const auto &p : setting_map) { + result.push_back(p.second); + } + return result; } /// Get the current value of a specified setting. template const T & GetValue(const std::string & name) const { - emp_assert(emp::Has(setting_map, name), name); - emp::Ptr base_ptr = setting_map.find(name)->second; - - // If we have a combo setting, determine the current value. - if (base_ptr->IsComboSetting()) { - emp::Ptr> ptr = base_ptr.Cast>(); - size_t id = cur_combo[ptr->id]; - return ptr->values[id]; - } + emp_assert(emp::Has(setting_map, name), name); + emp::Ptr base_ptr = setting_map.find(name)->second; + + // If we have a combo setting, determine the current value. + if (base_ptr->IsComboSetting()) { + emp::Ptr> ptr = base_ptr.Cast>(); + size_t id = cur_combo[ptr->id]; + return ptr->values[id]; + } - // Otherwise we have a regular setting. - return base_ptr.Cast>()->value; + // Otherwise we have a regular setting. + return base_ptr.Cast>()->value; } /// Scan through all values and return the maximum. template T MaxValue(const std::string & name) const { - emp_assert(emp::Has(setting_map, name), name); - emp::Ptr base_ptr = setting_map.find(name)->second; + emp_assert(emp::Has(setting_map, name), name); + emp::Ptr base_ptr = setting_map.find(name)->second; - // If we have a combo setting, determine the current value. - if (base_ptr->IsComboSetting()) { - emp::Ptr> ptr = base_ptr.Cast>(); - return emp::FindMax(ptr->values); - } + // If we have a combo setting, determine the current value. + if (base_ptr->IsComboSetting()) { + emp::Ptr> ptr = base_ptr.Cast>(); + return emp::FindMax(ptr->values); + } - // Otherwise we have a regular setting with just one value. - return base_ptr.Cast>()->value; + // Otherwise we have a regular setting with just one value. + return base_ptr.Cast>()->value; } /// Add a new setting of a specified type. Returns the (initially empty) vector of values @@ -320,13 +320,13 @@ namespace emp { emp::vector & AddComboSetting(const std::string & name, const std::string & desc="", const char option_flag='\0') { - emp_assert(!emp::Has(setting_map, name)); - auto new_ptr = emp::NewPtr>(name, desc, option_flag, "Values..."); - new_ptr->id = combo_settings.size(); - combo_settings.push_back(new_ptr); - setting_map[name] = new_ptr; - cur_combo.push_back(0); - return new_ptr->values; + emp_assert(!emp::Has(setting_map, name)); + auto new_ptr = emp::NewPtr>(name, desc, option_flag, "Values..."); + new_ptr->id = combo_settings.size(); + combo_settings.push_back(new_ptr); + setting_map[name] = new_ptr; + cur_combo.push_back(0); + return new_ptr->values; } /// A setting can also be linked to a value that is kept up-to-date. @@ -362,141 +362,141 @@ namespace emp { /// Access ALL values for a specified setting, to be modified freely. template emp::vector & ComboValues(const std::string & name) { - emp_assert(emp::Has(setting_map, name), name); - emp_assert(setting_map[name]->IsComboSetting()); - return setting_map[name].DynamicCast>()->values; + emp_assert(emp::Has(setting_map, name), name); + emp_assert(setting_map[name]->IsComboSetting()); + return setting_map[name].DynamicCast>()->values; } /// Start over stepping through all combinations of parameter values. void ResetCombos() { - // Setup as base combo. - for (size_t & x : cur_combo) x = 0; - combo_id = 0; + // Setup as base combo. + for (size_t & x : cur_combo) x = 0; + combo_id = 0; - // Setup all linked values. - for (auto x : combo_settings) x->SetValueID(0); + // Setup all linked values. + for (auto x : combo_settings) x->SetValueID(0); } /// Add a single new value to the specified setting. template void AddComboValue(const std::string & name, T && val) { - emp_assert(emp::Has(setting_map, name), name); - emp_assert(setting_map[name]->IsComboSetting()); - auto ptr = setting_map[name].DynamicCast>(); - ptr->values.emplace_back(std::forward(val)); + emp_assert(emp::Has(setting_map, name), name); + emp_assert(setting_map[name]->IsComboSetting()); + auto ptr = setting_map[name].DynamicCast>(); + ptr->values.emplace_back(std::forward(val)); } /// Set all values for the specified setting. template void SetComboValues(const std::string & name, T1 && val1, Ts &&... vals) { - emp_assert(emp::Has(setting_map, name)); - auto ptr = setting_map[name].DynamicCast>(); - emp::Append(ptr->values, std::forward(val1), std::forward(vals)...); + emp_assert(emp::Has(setting_map, name)); + auto ptr = setting_map[name].DynamicCast>(); + emp::Append(ptr->values, std::forward(val1), std::forward(vals)...); } /// Determine how many unique combinations there currently are. size_t CountCombos() { - size_t result = 1; - for (auto ptr : combo_settings) result *= ptr->GetSize(); - return result; + size_t result = 1; + for (auto ptr : combo_settings) result *= ptr->GetSize(); + return result; } /// Set the next combination of settings to be active. Return true if successful /// or false if we ran through all combinations and reset. bool NextCombo() { - combo_id++; - for (size_t i = 0; i < cur_combo.size(); i++) { - cur_combo[i]++; - - // Check if this new combo is valid. - if (cur_combo[i] < combo_settings[i]->GetSize()) { - combo_settings[i]->SetValueID( cur_combo[i] ); // Set value in linked variable. - return true; - } + combo_id++; + for (size_t i = 0; i < cur_combo.size(); i++) { + cur_combo[i]++; + + // Check if this new combo is valid. + if (cur_combo[i] < combo_settings[i]->GetSize()) { + combo_settings[i]->SetValueID( cur_combo[i] ); // Set value in linked variable. + return true; + } // Since it's not, prepare to move on to the next one. cur_combo[i] = 0; combo_settings[i]->SetValueID(0); } - // No valid combo found. - combo_id = 0; - return false; + // No valid combo found. + combo_id = 0; + return false; } /// Get the set of headers used for the CSV file. std::string GetSettingHeaders(const std::string & separator=",") { - std::string out_str; - for (auto [name,ptr] : setting_map) { - if (out_str.size()) out_str += separator; - out_str += ptr->name; - } - return out_str; + std::string out_str; + for (auto [name,ptr] : setting_map) { + if (out_str.size()) out_str += separator; + out_str += ptr->name; + } + return out_str; } /// Convert all of the current values into a comma-separated string. std::string CurSettings(const std::string & separator=",") const { - std::string out_str; - for (auto [name,ptr] : setting_map) { - if (ptr) { - if (out_str.size()) out_str += separator; - out_str += ptr->IsComboSetting() ? ptr->AsString(cur_combo[ptr->GetID()]) : ptr->AsString(); - } + std::string out_str; + for (auto [name,ptr] : setting_map) { + if (ptr) { + if (out_str.size()) out_str += separator; + out_str += ptr->IsComboSetting() ? ptr->AsString(cur_combo[ptr->GetID()]) : ptr->AsString(); + } } - return out_str; + return out_str; } /// Get the set of headers used for the CSV file. std::string GetComboHeaders(const std::string & separator=",") { - std::string out_string; - for (size_t i = 0; i < combo_settings.size(); i++) { - if (i) out_string += separator; - out_string += combo_settings[i]->name; - } - return out_string; + std::string out_string; + for (size_t i = 0; i < combo_settings.size(); i++) { + if (i) out_string += separator; + out_string += combo_settings[i]->name; + } + return out_string; } /// Convert all of the current values into a comma-separated string. std::string CurComboString(const std::string & separator=",", bool use_labels=false, ///< Print name with each value? bool multi_only=false ///< Only print values that can change? - ) const { - std::string out_str; - for (size_t i = 0; i < cur_combo.size(); i++) { - if (multi_only && combo_settings[i]->GetSize() == 1) continue; - if (out_str.size()) out_str += separator; - if (use_labels) out_str += emp::to_string(combo_settings[i]->name, '='); - out_str += combo_settings[i]->AsString(cur_combo[i]); - } - return out_str; + ) const { + std::string out_str; + for (size_t i = 0; i < cur_combo.size(); i++) { + if (multi_only && combo_settings[i]->GetSize() == 1) continue; + if (out_str.size()) out_str += separator; + if (use_labels) out_str += emp::to_string(combo_settings[i]->name, '='); + out_str += combo_settings[i]->AsString(cur_combo[i]); + } + return out_str; } /// Scan through all settings for a match option and return ID. emp::Ptr FindOptionMatch(const std::string & option_name) { - for (const auto & [name, ptr] : setting_map) { - if (ptr->IsOptionMatch(option_name)) return ptr; - } - return nullptr; + for (const auto & [name, ptr] : setting_map) { + if (ptr->IsOptionMatch(option_name)) return ptr; + } + return nullptr; } /// Scan through all settings for a match option and return ID. emp::Ptr FindFlagMatch(const char symbol) { - for (const auto & [name, ptr] : setting_map) { - if (ptr->IsFlagMatch(symbol)) return ptr; - } - return nullptr; + for (const auto & [name, ptr] : setting_map) { + if (ptr->IsFlagMatch(symbol)) return ptr; + } + return nullptr; } /// Take an input set of config options, process them, and track set of unprocessed ones. bool ProcessOptions(const emp::vector & args) { - exe_name = args[0]; + exe_name = args[0]; - for (size_t i = 1; i < args.size(); i++) { - const std::string & cur_arg = args[i]; - if (cur_arg.size() < 2 || cur_arg[0] != '-') { - unused_args.push_back(cur_arg); - continue; // If isn't an option, continue. - } + for (size_t i = 1; i < args.size(); i++) { + const std::string & cur_arg = args[i]; + if (cur_arg.size() < 2 || cur_arg[0] != '-') { + unused_args.push_back(cur_arg); + continue; // If isn't an option, continue. + } // See if this is a fully spelled-out option. auto setting_ptr = FindOptionMatch(cur_arg); @@ -528,49 +528,49 @@ namespace emp { } } - // Or see of this is a flag trigger. - else if (Has(action_map, cur_arg)) { - action_map[cur_arg].fun(); - } - - // Otherwise this argument will go unused; send it back. - else unused_args.push_back(cur_arg); + // Or see of this is a flag trigger. + else if (Has(action_map, cur_arg)) { + action_map[cur_arg].fun(); } - return true; + // Otherwise this argument will go unused; send it back. + else unused_args.push_back(cur_arg); + } + + return true; } bool ProcessOptions(int argc, char* argv[]) { - return ProcessOptions(emp::cl::args_to_strings(argc, argv)); + return ProcessOptions(emp::cl::args_to_strings(argc, argv)); } template void PrintHelp(const Ts &... examples) const { - std::cout << "Format: " << exe_name << " [OPTIONS...]\n" - << "\nSetting Options:\n"; - for (auto [name, ptr] : setting_map) { - std::string spacing(emp::Max(1, 12 - (int) ptr->args_label.size()), ' '); - std::cout << " -" << ptr->flag << " [" << ptr->args_label << "]" << spacing << ": " - << ptr->desc << " (--" << name << ") [" - << ptr->AsString() << "]\n"; + std::cout << "Format: " << exe_name << " [OPTIONS...]\n" + << "\nSetting Options:\n"; + for (auto [name, ptr] : setting_map) { + std::string spacing(emp::Max(1, 12 - (int) ptr->args_label.size()), ' '); + std::cout << " -" << ptr->flag << " [" << ptr->args_label << "]" << spacing << ": " + << ptr->desc << " (--" << name << ") [" + << ptr->AsString() << "]\n"; } std::cout << "\nAction Options:\n"; for (auto [name, action] : action_map) { - if (name.size() == 2) continue; // Skip flag entries. - std::cout << " -" << action.flag << " : " - << action.desc << " (" << name << ")\n"; + if (name.size() == 2) continue; // Skip flag entries. + std::cout << " -" << action.flag << " : " + << action.desc << " (" << name << ")\n"; } if constexpr (sizeof...(examples) > 0) { - std::cout << "\nExample: " << emp::to_string(examples...) << std::endl; + std::cout << "\nExample: " << emp::to_string(examples...) << std::endl; } std::cout.flush(); } -}; + }; } From f5e40914e8ea42394482482e37be30a767599517 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 18 Mar 2021 17:09:18 -0400 Subject: [PATCH 17/19] final formatting fixes --- include/emp/config/SettingConfig.hpp | 100 +++++++++++++-------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/include/emp/config/SettingConfig.hpp b/include/emp/config/SettingConfig.hpp index d4222c8791..f8851a07ea 100644 --- a/include/emp/config/SettingConfig.hpp +++ b/include/emp/config/SettingConfig.hpp @@ -71,31 +71,31 @@ namespace emp { emp::Ptr _var=nullptr) ///< Pointer to variable to set (optional) : SettingBase(_name, _desc, _flag, _arg), var_ptr(_var) { } - ~SettingInfo(){if(var_ptr){var_ptr.Delete();}} + ~SettingInfo(){if(var_ptr){var_ptr.Delete();}} - emp::Ptr Clone() const override { - emp::Ptr new_var_ptr = nullptr; - if (var_ptr) { - new_var_ptr = NewPtr(*var_ptr); - } - emp::Ptr> setting_info_ptr = emp::NewPtr>(name, desc, flag, args_label, new_var_ptr); - setting_info_ptr->value = this->value; - return setting_info_ptr; + emp::Ptr Clone() const override { + emp::Ptr new_var_ptr = nullptr; + if (var_ptr) { + new_var_ptr = NewPtr(*var_ptr); } + emp::Ptr> setting_info_ptr = emp::NewPtr>(name, desc, flag, args_label, new_var_ptr); + setting_info_ptr->value = this->value; + return setting_info_ptr; + } - size_t GetSize() const override { return 1; } - std::string AsString() const override { return emp::to_string(value); } - std::string AsString(size_t id) const override { - emp_assert(id == 0); - return emp::to_string(value); - } + size_t GetSize() const override { return 1; } + std::string AsString() const override { return emp::to_string(value); } + std::string AsString(size_t id) const override { + emp_assert(id == 0); + return emp::to_string(value); + } - bool FromString(const std::string_view & input) override { - value = emp::from_string(input); - // @CAO: Could do more tests to make sure whole string was used. - if (!var_ptr.IsNull()) *var_ptr = value; - return true; - } + bool FromString(const std::string_view & input) override { + value = emp::from_string(input); + // @CAO: Could do more tests to make sure whole string was used. + if (!var_ptr.IsNull()) *var_ptr = value; + return true; + } }; /// Allow a single setting to have multiple values specified that should be stepped through. @@ -106,10 +106,10 @@ namespace emp { size_t id; ///< Unique ID/position for this setting. ComboSettingInfo(const std::string & _name, ///< Unique name for this setting. - const std::string & _desc, ///< Description of this setting (for help) - const char _flag, ///< Char flag for easy access (e.g., "-h") - const std::string & _args, ///< Label for option arguments (for help) - emp::Ptr _var=nullptr) ///< Pointer to variable to set (optional) + const std::string & _desc, ///< Description of this setting (for help) + const char _flag, ///< Char flag for easy access (e.g., "-h") + const std::string & _args, ///< Label for option arguments (for help) + emp::Ptr _var=nullptr) ///< Pointer to variable to set (optional) : SettingBase(_name, _desc, _flag, _args), var_ptr(_var) { } ~ComboSettingInfo(){if(var_ptr){var_ptr.Delete();}} @@ -288,10 +288,10 @@ namespace emp { template T & AddSetting(const std::string & name, - const std::string & desc, - const char option_flag, - T & var, - const std::string & args_label="Value") + const std::string & desc, + const char option_flag, + T & var, + const std::string & args_label="Value") { emp_assert(!emp::Has(setting_map, name)); auto new_ptr = emp::NewPtr>(name, desc, option_flag, args_label, &var); @@ -318,8 +318,8 @@ namespace emp { template emp::vector & AddComboSetting(const std::string & name, - const std::string & desc="", - const char option_flag='\0') { + const std::string & desc="", + const char option_flag='\0') { emp_assert(!emp::Has(setting_map, name)); auto new_ptr = emp::NewPtr>(name, desc, option_flag, "Values..."); new_ptr->id = combo_settings.size(); @@ -332,10 +332,10 @@ namespace emp { /// A setting can also be linked to a value that is kept up-to-date. template emp::vector & AddComboSetting(const std::string & name, - const std::string & desc, - const char option_flag, - T & var, - const std::string & args_label="Values...") + const std::string & desc, + const char option_flag, + T & var, + const std::string & args_label="Values...") { emp_assert(!emp::Has(setting_map, name)); auto new_ptr = emp::NewPtr>(name, desc, option_flag, args_label, &var); @@ -429,7 +429,7 @@ namespace emp { std::string out_str; for (auto [name,ptr] : setting_map) { if (out_str.size()) out_str += separator; - out_str += ptr->name; + out_str += ptr->name; } return out_str; } @@ -440,7 +440,7 @@ namespace emp { for (auto [name,ptr] : setting_map) { if (ptr) { if (out_str.size()) out_str += separator; - out_str += ptr->IsComboSetting() ? ptr->AsString(cur_combo[ptr->GetID()]) : ptr->AsString(); + out_str += ptr->IsComboSetting() ? ptr->AsString(cur_combo[ptr->GetID()]) : ptr->AsString(); } } return out_str; @@ -451,7 +451,7 @@ namespace emp { std::string out_string; for (size_t i = 0; i < combo_settings.size(); i++) { if (i) out_string += separator; - out_string += combo_settings[i]->name; + out_string += combo_settings[i]->name; } return out_string; } @@ -555,22 +555,22 @@ namespace emp { std::cout << " -" << ptr->flag << " [" << ptr->args_label << "]" << spacing << ": " << ptr->desc << " (--" << name << ") [" << ptr->AsString() << "]\n"; - } + } - std::cout << "\nAction Options:\n"; - for (auto [name, action] : action_map) { - if (name.size() == 2) continue; // Skip flag entries. - std::cout << " -" << action.flag << " : " - << action.desc << " (" << name << ")\n"; - } + std::cout << "\nAction Options:\n"; + for (auto [name, action] : action_map) { + if (name.size() == 2) continue; // Skip flag entries. + std::cout << " -" << action.flag << " : " + << action.desc << " (" << name << ")\n"; + } - if constexpr (sizeof...(examples) > 0) { - std::cout << "\nExample: " << emp::to_string(examples...) << std::endl; - } + if constexpr (sizeof...(examples) > 0) { + std::cout << "\nExample: " << emp::to_string(examples...) << std::endl; + } - std::cout.flush(); + std::cout.flush(); } - }; + }; } From 1207299c7220689c8e32d33cadf0ac61de48a67f Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 18 Mar 2021 17:20:34 -0400 Subject: [PATCH 18/19] final formatting fixes --- .../SpatialCoop2017/source/SimplePDWorld.hpp | 2 +- include/emp/config/SettingConfig.hpp | 6 +- include/emp/prefab/QueueManager.hpp | 268 +++++++++--------- 3 files changed, 138 insertions(+), 138 deletions(-) diff --git a/demos/SpatialCoop2017/source/SimplePDWorld.hpp b/demos/SpatialCoop2017/source/SimplePDWorld.hpp index ff87a60e7c..bebaff0f0f 100644 --- a/demos/SpatialCoop2017/source/SimplePDWorld.hpp +++ b/demos/SpatialCoop2017/source/SimplePDWorld.hpp @@ -119,7 +119,7 @@ class SimplePDWorld { } void RunStep() { - for (size_t o = 0; o < N; o++) Repro(); + for (size_t o = 0; o < N; o++) Repro(); } size_t CountCoop(); diff --git a/include/emp/config/SettingConfig.hpp b/include/emp/config/SettingConfig.hpp index f8851a07ea..0e5fc664c6 100644 --- a/include/emp/config/SettingConfig.hpp +++ b/include/emp/config/SettingConfig.hpp @@ -130,8 +130,8 @@ namespace emp { std::stringstream ss; for (size_t i=0; i < values.size(); i++) { if (i) ss << ','; - ss << values[i]; - } + ss << values[i]; + } return ss.str(); } @@ -441,8 +441,8 @@ namespace emp { if (ptr) { if (out_str.size()) out_str += separator; out_str += ptr->IsComboSetting() ? ptr->AsString(cur_combo[ptr->GetID()]) : ptr->AsString(); - } } + } return out_str; } diff --git a/include/emp/prefab/QueueManager.hpp b/include/emp/prefab/QueueManager.hpp index 50a7997dd6..4950065160 100644 --- a/include/emp/prefab/QueueManager.hpp +++ b/include/emp/prefab/QueueManager.hpp @@ -101,88 +101,89 @@ namespace emp { /// Primary class that establishes queue for runs and processes them accordingly class QueueManager { - public: + public: - /// Information of each element within queue. This info represents the information required for each run to be processed. - struct RunInfo { - SettingConfig runinfo_config; // Holds all program-specific settings - size_t id; // The id of this run in the queue - size_t cur_epoch; // The current epoch that this run is on (will either be 0 or `epochs` unless this run is in progress) - size_t epochs; // The number of epochs this run is supposed to run for + /// Information of each element within queue. This info represents the information required for each run to be processed. + struct RunInfo { + SettingConfig runinfo_config; // Holds all program-specific settings + size_t id; // The id of this run in the queue + size_t cur_epoch; // The current epoch that this run is on (will either be 0 or `epochs` unless this run is in progress) + size_t epochs; // The number of epochs this run is supposed to run for - RunInfo(SettingConfig _config, size_t _id) - : runinfo_config(_config), id(_id), cur_epoch(0) { ; } + RunInfo(SettingConfig _config, size_t _id) + : runinfo_config(_config), id(_id), cur_epoch(0) { ; } - /// @returns current epoch - size_t GetEpoch() {return cur_epoch;} - /// @returns number of epochs to run for - size_t GetNEpochs() {return epochs;} - /// Increment current epoch by @param x - void IncEpoch(int x = 1) {cur_epoch += x;} - /// @returns configuration for this run - SettingConfig GetConfig() {return runinfo_config;} - }; + /// @returns current epoch + size_t GetEpoch() {return cur_epoch;} + /// @returns number of epochs to run for + size_t GetNEpochs() {return epochs;} + /// Increment current epoch by @param x + void IncEpoch(int x = 1) {cur_epoch += x;} + /// @returns configuration for this run + SettingConfig GetConfig() {return runinfo_config;} + }; - private: - SettingConfig queue_config; - std::queue runs; - emp::web::Div display_div; - emp::web::TextArea run_input; - emp::web::Button queue_button; - emp::web::Table display_table; + private: + SettingConfig queue_config; + std::queue runs; + emp::web::Div display_div; + emp::web::TextArea run_input; + emp::web::Button queue_button; + emp::web::Table display_table; - emp::vector ordered_metric_names; - emp::vector> metric_funs; + emp::vector ordered_metric_names; + emp::vector> metric_funs; - size_t num_runs = 10; - bool table_built = false; + size_t num_runs = 10; + bool table_built = false; - public: + public: /// @param user_config An example configuration file for this program. Used to initialize table headers. QueueManager(SettingConfig user_config) : queue_config(user_config) { ; } /// @returns True if queue is empty, false if it is not bool IsEmpty() { - return runs.empty(); + return runs.empty(); } /// @returns Number of runs remaining in the queue size_t RunsRemaining() { - return runs.size(); + return runs.size(); } /// Adds new run to queue using settings specified in @param settings. /// @param epochs indicates how many epochs this run should run for. void AddRun(SettingConfig settings, size_t epochs) { - RunInfo new_run(settings, runs.size()); - new_run.epochs = epochs; - runs.push(new_run); + RunInfo new_run(settings, runs.size()); + new_run.epochs = epochs; + runs.push(new_run); } /// Removes run from front of queue void RemoveRun() { - emp_assert(!IsEmpty(), "Queue is empty! Cannot remove!"); - runs.pop(); + emp_assert(!IsEmpty(), "Queue is empty! Cannot remove!"); + runs.pop(); } /// @returns The a reference to the first run in the queue /// (i.e. the one that is running currently or, if none are /// in progress, the next run) RunInfo& FrontRun() { - emp_assert(!IsEmpty(), "Queue is empty! Cannot access Front!"); - return runs.front(); + emp_assert(!IsEmpty(), "Queue is empty! Cannot access Front!"); + return runs.front(); } /// @returns the Div associated with this QueueManager. emp::web::Div GetDiv() { - return display_div; + return display_div; } + /// Clears the content of the div associated with this QueueManager void ResetDiv() { - display_div.Clear(); - table_built = false; + display_div.Clear(); + table_built = false; } /// Adds table containing information for this QueueManager to the @@ -194,95 +195,95 @@ class QueueManager { /// Note that you still need to add this div to your document, /// e.g. `my_doc << my_queue_manager.GetDiv()`; void BuildTable(const std::string & id = "") { - emp_assert(!table_built && - "Trying to add QueueManager table but QueueManager table already built"); - - // Get parameter names - emp::vector setting_names = queue_config.GetSettingMapNames(); - - // Total number of columns is number of params + number of metrics + - // a column for the run id and a column for the current epoch. - size_t col = 2 + setting_names.size() + ordered_metric_names.size(); - - // Make and style table - display_table = emp::web::Table(1, col, id); - display_table.SetCSS("border-collapse", "collapse"); - display_table.SetCSS("border", "3px solid black"); - display_table.CellsCSS("border", "1px solid black"); - - // Fill out header - display_table.GetCell(0, 0).SetHeader() << "Run"; - int column_count = 1; - for (const auto& p : setting_names) { - display_table.GetCell(0, column_count).SetHeader() << "" << p << ""; - ++column_count; - } - - display_table.GetCell(0, column_count).SetHeader() << "Epoch"; + emp_assert(!table_built && + "Trying to add QueueManager table but QueueManager table already built"); + + // Get parameter names + emp::vector setting_names = queue_config.GetSettingMapNames(); + + // Total number of columns is number of params + number of metrics + + // a column for the run id and a column for the current epoch. + size_t col = 2 + setting_names.size() + ordered_metric_names.size(); + + // Make and style table + display_table = emp::web::Table(1, col, id); + display_table.SetCSS("border-collapse", "collapse"); + display_table.SetCSS("border", "3px solid black"); + display_table.CellsCSS("border", "1px solid black"); + + // Fill out header + display_table.GetCell(0, 0).SetHeader() << "Run"; + int column_count = 1; + for (const auto& p : setting_names) { + display_table.GetCell(0, column_count).SetHeader() << "" << p << ""; ++column_count; + } - // if adding more features after this point, keep in mind of where - // the col count will be - for (size_t i = 0; i < ordered_metric_names.size(); i++) { - display_table.GetCell(0, column_count + i).SetHeader() << ordered_metric_names[i]; - } + display_table.GetCell(0, column_count).SetHeader() << "Epoch"; + ++column_count; - display_div << display_table; - table_built = true; + // if adding more features after this point, keep in mind of where + // the col count will be + for (size_t i = 0; i < ordered_metric_names.size(); i++) { + display_table.GetCell(0, column_count + i).SetHeader() << ordered_metric_names[i]; + } + + display_div << display_table; + table_built = true; } /// Helper function to add the last run in the queue to the table /// Called by queue button. void AddNewQueuedRunToTable() { - emp_assert(table_built && - "Trying to add run to QueueManager table but table hasn't been initialized. Call BuildTable first."); - - // Update the table. - int line_id = display_table.GetNumRows(); - display_table.Rows(line_id + 1); - int col_count = 0; - display_table.GetCell(line_id, col_count) << runs.back().id; - - // Add correct parameter values - for (auto p : runs.back().runinfo_config.GetSettingMapBase()) { - display_table.GetCell(line_id, ++col_count) << (*p).AsString(); - } - - // Add placeholders for metrics and epoch column - for (int i = 0; i < ordered_metric_names.size() + 1; i++) { - display_table.GetCell(line_id, ++col_count) << "Waiting..."; - } - - // Draw the new table. - display_table.CellsCSS("border", "1px solid black"); - display_table.Redraw(); + emp_assert(table_built && + "Trying to add run to QueueManager table but table hasn't been initialized. Call BuildTable first."); + + // Update the table. + int line_id = display_table.GetNumRows(); + display_table.Rows(line_id + 1); + int col_count = 0; + display_table.GetCell(line_id, col_count) << runs.back().id; + + // Add correct parameter values + for (auto p : runs.back().runinfo_config.GetSettingMapBase()) { + display_table.GetCell(line_id, ++col_count) << (*p).AsString(); + } + + // Add placeholders for metrics and epoch column + for (int i = 0; i < ordered_metric_names.size() + 1; i++) { + display_table.GetCell(line_id, ++col_count) << "Waiting..."; + } + + // Draw the new table. + display_table.CellsCSS("border", "1px solid black"); + display_table.Redraw(); } /// Update QueueManager to reflect current status of runs and metrics. /// Handles updating table and updating queue (checking if current run is done). void Update() { - emp_assert(table_built && - "Trying to update QueueManager table but table hasn't been initialized. Call BuildTable first."); + emp_assert(table_built && + "Trying to update QueueManager table but table hasn't been initialized. Call BuildTable first."); - size_t id = FrontRun().id; - RunInfo& current_run = FrontRun(); + size_t id = FrontRun().id; + RunInfo& current_run = FrontRun(); - size_t n_settings = queue_config.GetSettingMapNames().size(); + size_t n_settings = queue_config.GetSettingMapNames().size(); - display_table.Freeze(); - display_table.GetCell(id + 1, n_settings+1).ClearChildren() << emp::to_string(current_run.cur_epoch); + display_table.Freeze(); + display_table.GetCell(id + 1, n_settings+1).ClearChildren() << emp::to_string(current_run.cur_epoch); - // user function configuration - for (int i = 0; i < metric_funs.size(); i++) { - display_table.GetCell(id + 1, n_settings + 2 + i).ClearChildren() << metric_funs[i](); - } + // user function configuration + for (int i = 0; i < metric_funs.size(); i++) { + display_table.GetCell(id + 1, n_settings + 2 + i).ClearChildren() << metric_funs[i](); + } - if (current_run.cur_epoch >= current_run.epochs) { // Are we done with this run? - RemoveRun(); // Updates to the next run - } + if (current_run.cur_epoch >= current_run.epochs) { // Are we done with this run? + RemoveRun(); // Updates to the next run + } - display_table.Activate(); + display_table.Activate(); } @@ -295,19 +296,19 @@ class QueueManager { /// @param get_epochs is a function that will be used to determine how many epochs/time steps /// the run is supposed to go for void AddQueueButton(std::function get_conf, std::function get_epochs) { - run_input = emp::web::TextArea([this](const std::string & str){ - this->num_runs = emp::from_string(str); - }, "run_count"); - run_input.SetText(emp::to_string(num_runs)); - display_div << run_input; - - queue_button = emp::web::Button([this, get_conf, get_epochs]() { - for (int i = 0; i < this->num_runs; i++) { - AddRun(get_conf(), get_epochs()); - AddNewQueuedRunToTable(); - } - }, "Queue", "queue_but"); - display_div << queue_button; + run_input = emp::web::TextArea([this](const std::string & str){ + this->num_runs = emp::from_string(str); + }, "run_count"); + run_input.SetText(emp::to_string(num_runs)); + display_div << run_input; + + queue_button = emp::web::Button([this, get_conf, get_epochs]() { + for (int i = 0; i < this->num_runs; i++) { + AddRun(get_conf(), get_epochs()); + AddNewQueuedRunToTable(); + } + }, "Queue", "queue_but"); + display_div << queue_button; } /// Adds new metric to table @@ -317,15 +318,14 @@ class QueueManager { /// @param header_name the name this column should have in the table /// void AddMetric(std::function func, std::string header_name) { - ordered_metric_names.push_back(header_name); - metric_funs.push_back(func); - - if (table_built) { - size_t col_id = display_table.GetNumCols(); - display_table.Cols(col_id + 1); - display_table.GetCell(0, col_id).SetHeader() << header_name; - } + ordered_metric_names.push_back(header_name); + metric_funs.push_back(func); + + if (table_built) { + size_t col_id = display_table.GetNumCols(); + display_table.Cols(col_id + 1); + display_table.GetCell(0, col_id).SetHeader() << header_name; + } } -}; - + }; } // namespace emp From 32121ab7578bfa0adcc1089ef7a9ce8907ed7be6 Mon Sep 17 00:00:00 2001 From: Emily Dolson Date: Thu, 18 Mar 2021 17:30:24 -0400 Subject: [PATCH 19/19] final formatting fixes --- .../source/web/SimplePDWorld-web.cpp | 237 +++++++++--------- 1 file changed, 118 insertions(+), 119 deletions(-) diff --git a/demos/SpatialCoop2017/source/web/SimplePDWorld-web.cpp b/demos/SpatialCoop2017/source/web/SimplePDWorld-web.cpp index a8b6f1b630..755aa4b30c 100644 --- a/demos/SpatialCoop2017/source/web/SimplePDWorld-web.cpp +++ b/demos/SpatialCoop2017/source/web/SimplePDWorld-web.cpp @@ -1,14 +1,13 @@ // This file is part of Project Name // Copyright (C) Michigan State University, 2017. // Released under the MIT Software license; see doc/LICENSE -// PD WORLD EXAMPLE #include #include +#include "emp/web/web.hpp" #include "emp/prefab/QueueManager.hpp" #include "../SimplePDWorld.hpp" -#include "emp/web/web.hpp" namespace UI = emp::web; @@ -21,144 +20,144 @@ int cur_x = -1; int cur_y = -1; std::function SetupConfig = [](){ - emp::SettingConfig config; - config.AddSetting("r") = {world.GetR()}; - config.AddSetting("u") = {world.GetU()}; - config.AddSetting("N") = {world.GetN()}; - config.AddSetting("E") = {world.GetE()}; + emp::SettingConfig config; + config.AddSetting("r") = {world.GetR()}; + config.AddSetting("u") = {world.GetU()}; + config.AddSetting("N") = {world.GetN()}; + config.AddSetting("E") = {world.GetE()}; - return config; + return config; }; emp::SettingConfig config = SetupConfig(); emp::QueueManager run_list(config); void DrawCanvas() { - UI::Canvas canvas = doc.Canvas("canvas"); - canvas.Clear("black"); + UI::Canvas canvas = doc.Canvas("canvas"); + canvas.Clear("black"); - const emp::vector& pop = world.GetPop(); + const emp::vector & pop = world.GetPop(); - if (cur_x >= 0) { - canvas.Circle(cur_x, cur_y, world_size * world.GetR(), "pink"); - } + if (cur_x >= 0) { + canvas.Circle(cur_x, cur_y, world_size*world.GetR(), "pink"); + } - for (const Org& org : pop) { - if (org.coop) { - canvas.Circle(org.x * world_size, org.y * world_size, 2, "blue", "#8888FF"); - } else { - canvas.Circle(org.x * world_size, org.y * world_size, 2, "#FF8888", "red"); - } + for (const Org & org : pop) { + if (org.coop) { + canvas.Circle(org.x*world_size, org.y*world_size, 2, "blue", "#8888FF"); + } else { + canvas.Circle(org.x*world_size, org.y*world_size, 2, "#FF8888", "red"); } + } - doc.Text("ud_text").Redraw(); + doc.Text("ud_text").Redraw(); } void CanvasClick(int x, int y) { - cur_x = x; - cur_y = y; - DrawCanvas(); + cur_x = x; + cur_y = y; + DrawCanvas(); } -void TogglePlay() { - auto& anim = doc.Animate("anim_world"); - anim.ToggleActive(); - auto but = doc.Button("start_but"); - if (anim.GetActive()) - but.SetLabel("Pause"); - else - but.SetLabel("Start"); - - but = doc.Button("run_but"); - if (anim.GetActive()) - but.SetLabel("Stop"); - else - but.SetLabel("Fast Forward!"); +void TogglePlay() +{ + auto & anim = doc.Animate("anim_world"); + anim.ToggleActive(); + auto but = doc.Button("start_but"); + if (anim.GetActive()) but.SetLabel("Pause"); + else but.SetLabel("Start"); + + but = doc.Button("run_but"); + if (anim.GetActive()) but.SetLabel("Stop"); + else but.SetLabel("Fast Forward!"); } int anim_step = 1; -int main() { - - std::function coop_func = []() { return std::to_string(world.CountCoop()); }; - std::function defect_func = []() { return std::to_string(run_list.FrontRun().runinfo_config.GetValue("N") - world.CountCoop()); }; - - run_list.AddMetric(coop_func, "Num Coop"); - run_list.AddMetric(defect_func, "Num Defect"); - doc << "

Spatial Prisoner's Dillemma

"; - auto canvas = doc.AddCanvas(world_size, world_size, "canvas"); - // canvas.On("click", CanvasClick); - doc.AddAnimation("anim_world", []() { - // if queue has runs - if (!run_list.IsEmpty()) { - emp::QueueManager::RunInfo & run = run_list.FrontRun(); // Referencing current run - if (run.GetEpoch() == 0) { // Are we starting a new run? - world.Setup(run.runinfo_config.GetValue("r"), run.runinfo_config.GetValue("u"), run.runinfo_config.GetValue("N"), run.runinfo_config.GetValue("E")); - DrawCanvas(); - } - run.IncEpoch(anim_step); - } - world.Run(anim_step); +int main() +{ + doc << "

Spatial Prisoner's Dillemma

"; + std::function coop_func = []() { return std::to_string(world.CountCoop()); }; + std::function defect_func = []() { return std::to_string(run_list.FrontRun().runinfo_config.GetValue("N") - world.CountCoop()); }; + + run_list.AddMetric(coop_func, "Num Coop"); + run_list.AddMetric(defect_func, "Num Defect"); + + auto canvas = doc.AddCanvas(world_size, world_size, "canvas"); + // canvas.On("click", CanvasClick); + doc.AddAnimation("anim_world", [](){ + // if queue has runs + if (!run_list.IsEmpty()) { + emp::QueueManager::RunInfo & run = run_list.FrontRun(); // Referencing current run + if (run.GetEpoch() == 0) { // Are we starting a new run? + world.Setup(run.runinfo_config.GetValue("r"), run.runinfo_config.GetValue("u"), run.runinfo_config.GetValue("N"), run.runinfo_config.GetValue("E")); DrawCanvas(); - if (!run_list.IsEmpty()) { - run_list.Update(); //calculations for table - } - }); - - doc << "
"; - doc.AddButton([]() { - anim_step = 1; - TogglePlay(); - }, "Play", "start_but"); - doc.AddButton([]() { world.Run(1); DrawCanvas(); }, "Step", "step_but"); - doc.AddButton([]() { - anim_step = 100; - TogglePlay(); - }, "Fast Forward!", "run_but"); - doc.AddButton([]() { world.Reset(); DrawCanvas(); }, "Randomize", "rand_but"); - auto ud_text = doc.AddText("ud_text"); - ud_text << " Epoch = " << UI::Live(world.epoch); - - doc << "
Radius (r) = "; - doc.AddTextArea([](const std::string& str) { - double r = emp::from_string(str); - world.SetR(r); - }, "r_set").SetText(emp::to_string(world.GetR())); - - doc << "
cost/benefit ratio (u) = "; - doc.AddTextArea([](const std::string& str) { - double u = emp::from_string(str); - world.SetU(u); - }, "u_set").SetText(emp::to_string(world.GetU())); - - doc << "
Population Size (N) = "; - doc.AddTextArea([](const std::string& str) { - size_t N = emp::from_string(str); - world.SetN(N); - }, "N_set").SetText(emp::to_string(world.GetN())); - - doc << "
Num Epochs on Run (E) = "; - doc.AddTextArea([](const std::string& str) { - size_t E = emp::from_string(str); - world.SetE(E); - }, "E_set").SetText(emp::to_string(world.GetE())); - - doc << "
" - << "NOTE: You must hit 'Randomize' after changing any parameters for them to take effect." - << "
" - << "

Full Runs

" - << "You can perform many runs at once with the same configuration. " - << "Setup the configuration above, choose the number of runs, and queue them up (as many as you like, even with different parameters). " - << "The next time you start (or fast forward) above, it will start working its way through the queued runs. " - << "
" - << "How many runs? "; - - run_list.AddQueueButton([](){return SetupConfig();}, [](){return world.GetE();}); - - doc << "
"; - - doc << run_list.GetDiv(); - run_list.BuildTable(); - + } + run.IncEpoch(anim_step); + } + world.Run(anim_step); DrawCanvas(); + if (!run_list.IsEmpty()) { + run_list.Update(); //calculations for table + } + }); + + doc << "
"; + doc.AddButton([](){ + anim_step = 1; + TogglePlay(); + }, "Play", "start_but"); + doc.AddButton([](){ world.Run(1); DrawCanvas(); }, "Step", "step_but"); + doc.AddButton([](){ + anim_step = 100; + TogglePlay(); + }, "Fast Forward!", "run_but"); + doc.AddButton([](){ world.Reset(); DrawCanvas(); }, "Randomize", "rand_but"); + auto ud_text = doc.AddText("ud_text"); + ud_text << " Epoch = " << UI::Live(world.epoch); + + doc << "
Radius (r) = "; + doc.AddTextArea([](const std::string & str){ + double r = emp::from_string(str); + world.SetR(r); + }, "r_set").SetText(emp::to_string(world.GetR())); + + doc << "
cost/benefit ratio (u) = "; + doc.AddTextArea([](const std::string & str){ + double u = emp::from_string(str); + world.SetU(u); + }, "u_set").SetText(emp::to_string(world.GetU())); + + + doc << "
Population Size (N) = "; + doc.AddTextArea([](const std::string & str){ + size_t N = emp::from_string(str); + world.SetN(N); + }, "N_set").SetText(emp::to_string(world.GetN())); + + + doc << "
Num Epochs on Run (E) = "; + doc.AddTextArea([](const std::string & str){ + size_t E = emp::from_string(str); + world.SetE(E); + }, "E_set").SetText(emp::to_string(world.GetE())); + + doc << "
" + << "NOTE: You must hit 'Randomize' after changing any parameters for them to take effect." + << "
" + << "

Full Runs

" + << "You can perform many runs at once with the same configuration. " + << "Setup the configuration above, choose the number of runs, and queue them up (as many as you like, even with different parameters). " + << "The next time you start (or fast forward) above, it will start working its way through the queued runs. " + << "
" + << "How many runs? "; + + run_list.AddQueueButton([](){return SetupConfig();}, [](){return world.GetE();}); + + doc << "
"; + + doc << run_list.GetDiv(); + run_list.BuildTable(); + + DrawCanvas(); } \ No newline at end of file