diff --git a/eo/contrib/irace/CMakeLists.txt b/eo/contrib/irace/CMakeLists.txt new file mode 100644 index 000000000..37c82187e --- /dev/null +++ b/eo/contrib/irace/CMakeLists.txt @@ -0,0 +1,97 @@ + +###################################################################################### +# Project settings +###################################################################################### + +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) + +project("paradiseo-irace") + +enable_language(CXX) # C++ +set(CMAKE_CXX_STANDARD 17) + +## Current version +set(VERSION_MAJOR 0 CACHE STRING "Major version number" ) +set(VERSION_MINOR 1 CACHE STRING "Minor version number" ) +set(VERSION_PATCH 0 CACHE STRING "Patch version number" ) +mark_as_advanced(VERSION_MAJOR VERSION_MINOR VERSION_PATCH) + + +###################################################################################### +# Configurable user settings +###################################################################################### + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic") + +# put binaries in the build directory +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +# Dump used compiler flags. +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + + +###################################################################################### +# Dependencies +###################################################################################### + +# ParadisEO +set(PARADISEO_ROOT "../../../../paradiseo" CACHE PATH "Where to find ParadisEO") +set(PARADISEO_BUILD "${PARADISEO_ROOT}/build" CACHE PATH "Build dir of ParadisEO") + +include_directories(${PARADISEO_ROOT}) +include_directories(${PARADISEO_ROOT}/eo/src) +link_directories(${PARADISEO_BUILD}/lib) +set(PARADISEO_LIBRARIES ga eoutils eo) + +# IOH +set(IOH_ROOT "~/code/IOHexperimenter/" CACHE PATH "Where to find IOHexperimenter") +find_path(IOH_PROBLEM_H "IOHprofiler_problem.h" PATHS ${IOH_ROOT}/src/Template/) +find_library(IOH_LIBRARY "IOH" PATHS ${IOH_ROOT} PATH_SUFFIXES release Release debug Debug build Build) + +if(EXISTS ${IOH_PROBLEM_H} AND EXISTS ${IOH_LIBRARY}) + message(STATUS "Found IOH in ${IOH_ROOT}") + include_directories(${IOH_ROOT}/build/Cpp/src/) + link_directories(${IOH_ROOT}/build/Cpp/bin/) + + # Workaround IOH's poorly designed headers inclusion scheme. + SET(PROBLEMS_BBOB_DIR "src/Problems/BBOB") + SET(PROBLEMS_BBOB_COMMON_DIR "src/Problems/BBOB/bbob_common_used_functions") + SET(PROBLEMS_COMMON_DIR "src/Problems/common_used_functions") + SET(PROBLEMS_PBO_DIR "src/Problems/PBO") + SET(PROBLEMS_WMODEL_DIR "src/Problems/WModel") + SET(PROBLEMS_PYTHON_DIR "src/Problems/Python") + SET(SUITES_DIR "src/Suites") + SET(TEMPLATE_DIR "src/Template") + SET(TEMPLATE_EXPERIMENTS_DIR "src/Template/Experiments") + SET(TEMPLATE_LOGGERS_DIR "src/Template/Loggers") + SET(IOHEXPERIMENTER_DIR + "${IOH_ROOT}/${PROBLEMS_COMMON_DIR}" + "${IOH_ROOT}/${PROBLEMS_BBOB_DIR}" + "${IOH_ROOT}/${PROBLEMS_BBOB_COMMON_DIR}" + "${IOH_ROOT}/${PROBLEMS_PBO_DIR}" + "${IOH_ROOT}/${PROBLEMS_WMODEL_DIR}" + "${IOH_ROOT}/${PROBLEMS_PYTHON_DIR}" + "${IOH_ROOT}/${SUITES_DIR}" + "${IOH_ROOT}/${TEMPLATE_DIR}" + "${IOH_ROOT}/${TEMPLATE_EXPERIMENTS_DIR}" + "${IOH_ROOT}/${TEMPLATE_LOGGERS_DIR}" + ) + include_directories(${IOHEXPERIMENTER_DIR}) + +else() + if(NOT EXISTS ${IOH_PROBLEM_H}) + message(FATAL_ERROR "Could not find `IOHprofiler_problem.h` in: ${IOH_ROOT}/src/Template/ (did you forget to compile it?)") + endif() + if(NOT EXISTS ${IOH_LIBRARIES}) + message(FATAL_ERROR "Could not find `libIOH` in: ${IOH_ROOT}/[release|debug|build] (did you forget to compile it?)") + endif() +endif() + + +###################################################################################### +# Start building +###################################################################################### + +add_executable(fastga fastga.cpp) +target_link_libraries(fastga ${PARADISEO_LIBRARIES} ${IOH_LIBRARY}) + diff --git a/eo/contrib/irace/fastga.cpp b/eo/contrib/irace/fastga.cpp new file mode 100644 index 000000000..919231eb7 --- /dev/null +++ b/eo/contrib/irace/fastga.cpp @@ -0,0 +1,417 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// using Particle = eoRealParticle; +using Ints = eoInt, size_t>; +using Bits = eoBit, int>; + +// by enumerating candidate operators and their parameters. +eoAlgoFoundryFastGA& make_foundry( + eoFunctorStore& store, + eoInit& init, + eoEvalFunc& eval_onemax, + const size_t max_evals, + const size_t generations + ) +{ + // FIXME using max_restarts>1 does not allow to honor max evals. + auto& foundry = store.pack< eoAlgoFoundryFastGA >(init, eval_onemax, max_evals, /*max_restarts=*/1); + + /***** Continuators ****/ + foundry.continuators.add< eoGenContinue >(generations); + // for(size_t i=1; i<10; i++) { + // foundry.continuators.add< eoGenContinue >(i); + // } + // for(size_t i=10; i < 100; i+=2 ) { + // foundry.continuators.add< eoSteadyFitContinue >(10,i); + // } + + for(double i=0.0; i<1.0; i+=0.2) { + foundry.crossover_rates.add(i); + foundry.mutation_rates.add(i); + } + + /***** Offsprings size *****/ + // for(size_t i=5; i<100; i+=10) { + // foundry.offspring_sizes.add(i); + // } + + foundry.offspring_sizes.add(0); // 0 = use parents fixed pop size. + + /***** Crossovers ****/ + for(double i=0.1; i<1.0; i+=0.2) { + foundry.crossovers.add< eoUBitXover >(i); // preference over 1 + } + for(size_t i=1; i < 10; i+=2) { + + foundry.crossovers.add< eoNPtsBitXover >(i); // nb of points + } + foundry.crossovers.add< eo1PtBitXover >(); + + /***** Mutations ****/ + double p = 1.0; // Probability of flipping eath bit. + foundry.mutations.add< eoUniformBitMutation >(p); // proba of flipping k bits, k drawn in uniform distrib + foundry.mutations.add< eoStandardBitMutation >(p); // proba of flipping k bits, k drawn in binomial distrib + foundry.mutations.add< eoConditionalBitMutation >(p); // proba of flipping k bits, k drawn in binomial distrib, minus zero + foundry.mutations.add< eoShiftedBitMutation >(p); // proba of flipping k bits, k drawn in binomial distrib, changing zeros to one + foundry.mutations.add< eoNormalBitMutation >(p); // proba of flipping k bits, k drawn in normal distrib + foundry.mutations.add< eoFastBitMutation >(p); // proba of flipping k bits, k drawn in powerlaw distrib + for(size_t i=1; i < 11; i+=2) { + foundry.mutations.add< eoDetSingleBitFlip >(i); // mutate k bits without duplicates + } + + /***** Selectors *****/ + for(eoOperatorFoundry>& ops : + {std::ref(foundry.crossover_selectors), + std::ref(foundry.mutation_selectors) }) { + + ops.add< eoRandomSelect >(); + ops.add< eoStochTournamentSelect >(0.5); + ops.add< eoSequentialSelect >(); + ops.add< eoProportionalSelect >(); + for(size_t i=2; i < 11; i+=4) { + ops.add< eoDetTournamentSelect >(i); + } + } + + foundry.aftercross_selectors.add< eoRandomSelect >(); + + + /***** Replacements ****/ + foundry.replacements.add< eoPlusReplacement >(); + foundry.replacements.add< eoCommaReplacement >(); + foundry.replacements.add< eoSSGAWorseReplacement >(); + for(double i=0.51; i<0.92; i+=0.2) { + foundry.replacements.add< eoSSGAStochTournamentReplacement >(i); + } + for(size_t i=2; i < 11; i+=2) { + foundry.replacements.add< eoSSGADetTournamentReplacement >(i); + } + + return foundry; +} + +Bits::Fitness fake_func(const Bits&) { return 0; } + +void print_param_range(const eoParam& param, const size_t slot_size, std::ostream& out = std::cout) +{ + // If there is no choice to be made on this operator, comment it out. + if(slot_size - 1 <= 0) { + out << "# "; + } + + // irace doesn't support "-" in names. + std::string irace_name = param.longName(); + irace_name.erase(std::remove(irace_name.begin(), irace_name.end(), '-'), irace_name.end()); + + out << irace_name + << "\t\"--" << param.longName() << "=\"" + << "\ti"; + + if(slot_size -1 <= 0) { + out << "\t(0)"; + } else { + out << "\t(0," << slot_size-1 << ")"; + } + out << std::endl; +} + +int main(int argc, char* argv[]) +{ + /***** Global parameters. *****/ + enum { NO_ERROR = 0, ERROR_USAGE = 100 }; + + eoFunctorStore store; + + eoParser parser(argc, argv, "FastGA interface for iRace"); + + const size_t dimension = parser.getORcreateParam(1000, + "dimension", "Dimension size", + 'd', "Problem").value(); + + const size_t max_evals = parser.getORcreateParam(2 * dimension, + "max-evals", "Maximum number of evaluations", + 'e', "Stopping criterion").value(); + + const size_t buckets = parser.getORcreateParam(100, + "buckets", "Number of buckets for discretizing the ECDF", + 'b', "Performance estimation").value(); + + uint32_t seed = + parser.getORcreateParam(0, + "seed", "Random number seed (0 = epoch)", + 'S').value(); + if(seed == 0) { + seed = time(0); + } + // rng is a global + rng.reseed(seed); + + + auto problem_p = parser.getORcreateParam(0, + "problem", "Problem ID", + 'p', "Problem", /*required=*/true); + const size_t problem = problem_p.value(); + + + auto pop_size_p = parser.getORcreateParam(1, + "pop-size", "Population size", + 'P', "Operator Choice", /*required=*/false); + const size_t pop_size = pop_size_p.value(); + + auto instance_p = parser.getORcreateParam(0, + "instance", "Instance ID", + 'i', "Instance", /*required=*/false); + const size_t instance = instance_p.value(); + + auto continuator_p = parser.getORcreateParam(0, + "continuator", "Stopping criterion", + 'o', "Operator Choice", /*required=*/false); // Single alternative, not required. + const size_t continuator = continuator_p.value(); + + auto crossover_rate_p = parser.getORcreateParam(0, + "crossover-rate", "", + 'C', "Operator Choice", /*required=*/true); + const size_t crossover_rate = crossover_rate_p.value(); + + auto crossover_selector_p = parser.getORcreateParam(0, + "cross-selector", "How to selects candidates for cross-over", + 's', "Operator Choice", /*required=*/true); + const size_t crossover_selector = crossover_selector_p.value(); + + auto crossover_p = parser.getORcreateParam(0, + "crossover", "", + 'c', "Operator Choice", /*required=*/true); + const size_t crossover = crossover_p.value(); + + auto aftercross_selector_p = parser.getORcreateParam(0, + "aftercross-selector", "How to selects between the two individuals altered by cross-over which one will mutate", + 'a', "Operator Choice", /*required=*/false); // Single alternative, not required. + const size_t aftercross_selector = aftercross_selector_p.value(); + + auto mutation_rate_p = parser.getORcreateParam(0, + "mutation-rate", "", + 'M', "Operator Choice", /*required=*/true); + const size_t mutation_rate = mutation_rate_p.value(); + + auto mutation_selector_p = parser.getORcreateParam(0, + "mut-selector", "How to selects candidate for mutation", + 'u', "Operator Choice", /*required=*/true); + const size_t mutation_selector = mutation_selector_p.value(); + + auto mutation_p = parser.getORcreateParam(0, + "mutation", "", + 'm', "Operator Choice", /*required=*/true); + const size_t mutation = mutation_p.value(); + + auto replacement_p = parser.getORcreateParam(0, + "replacement", "", + 'r', "Operator Choice", /*required=*/true); + const size_t replacement = replacement_p.value(); + + auto offspring_size_p = parser.getORcreateParam(0, + "offspring-size", "Offsprings size (0 = same size than the parents pop, see --pop-size)", + 'O', "Operator Choice", /*required=*/false); // Single alternative, not required. + const size_t offspring_size = offspring_size_p.value(); + + + // Help + Verbose routines + make_verbose(parser); + make_help(parser, /*exit_after*/false, std::clog); + + if(parser.userNeedsHelp()) { + + // Fake operators, just to be able to call make_foundry + // to get the configured operators slots. + eoEvalFuncPtr fake_eval(fake_func); + eoUniformGenerator fake_gen(0, 1); + eoInitFixedLength fake_init(/*bitstring size=*/1, fake_gen); + auto fake_foundry = make_foundry(store, fake_init, fake_eval, max_evals, /*generations=*/ 1); + + size_t n = + fake_foundry.crossover_rates.size() + * fake_foundry.crossover_selectors.size() + * fake_foundry.crossovers.size() + * fake_foundry.aftercross_selectors.size() + * fake_foundry.mutation_rates.size() + * fake_foundry.mutation_selectors.size() + * fake_foundry.mutations.size() + * fake_foundry.replacements.size() + * fake_foundry.continuators.size() + * fake_foundry.offspring_sizes.size(); + std::clog << std::endl; + std::clog << n << " possible algorithms configurations." << std::endl; + + std::clog << "Ranges of configurable parameters (redirect the stdout in a file to use it with iRace): " << std::endl; + + // Do not print problem and instances, as they are managed separately by irace. + std::cout << "# name\tswitch\ttype\trange" << std::endl; + print_param_range( continuator_p, fake_foundry.continuators .size(), std::cout); + print_param_range( crossover_rate_p, fake_foundry.crossover_rates .size(), std::cout); + print_param_range( crossover_selector_p, fake_foundry.crossover_selectors .size(), std::cout); + print_param_range(aftercross_selector_p, fake_foundry.aftercross_selectors.size(), std::cout); + print_param_range( crossover_p, fake_foundry.crossovers .size(), std::cout); + print_param_range( mutation_rate_p, fake_foundry.mutation_rates .size(), std::cout); + print_param_range( mutation_selector_p, fake_foundry.mutation_selectors .size(), std::cout); + print_param_range( mutation_p, fake_foundry.mutations .size(), std::cout); + print_param_range( replacement_p, fake_foundry.replacements .size(), std::cout); + print_param_range( offspring_size_p, fake_foundry.offspring_sizes .size(), std::cout); + + // std::ofstream irace_param("fastga.params"); + // irace_param << "# name\tswitch\ttype\tvalues" << std::endl; + + exit(NO_ERROR); + } + + const size_t generations = static_cast(std::floor( + static_cast(max_evals) / static_cast(pop_size))); + // const size_t generations = std::numeric_limits::max(); + eo::log << eo::debug << "Number of generations: " << generations << std::endl; + + // Problem configuration code. + struct Problem { + double dummy; + size_t epistasis; + size_t neutrality; + size_t ruggedness; + size_t max_target; + }; + + std::map problem_config_mapping { + { 0, {0, 0, 1, 0, 1000}}, + { 1, {0, 0, 3, 0, 333}}, + { 2, {0, 0, 5, 0, 200}}, + { 3, {0, 2, 1, 0, 1000}}, + { 4, {0, 2, 3, 0, 333}}, + { 5, {0, 2, 3, 0, 200}}, + { 6, {0, 4, 1, 0, 1000}}, + { 7, {0, 4, 3, 0, 333}}, + { 8, {0, 4, 5, 0, 200}}, + { 9, {0.5, 0, 1, 0, 500}}, + {10, {0.5, 0, 3, 0, 166}}, + {11, {0.5, 0, 5, 0, 100}}, + {12, {0.5, 2, 1, 0, 500}}, + {13, {0.5, 2, 3, 0, 166}}, + {14, {0.5, 2, 5, 0, 100}}, + {15, {0.5, 4, 1, 0, 500}}, + {16, {0.5, 4, 3, 0, 166}}, + {17, {0.5, 4, 5, 0, 100}}, + }; + + assert(0 <= problem and problem < problem_config_mapping.size()); + + /***** IOH logger *****/ + auto max_target_para = problem_config_mapping[problem].max_target; + IOHprofiler_RangeLinear target_range(0, max_target_para, buckets); + IOHprofiler_RangeLinear budget_range(0, max_evals, buckets); + IOHprofiler_ecdf_logger logger( + target_range, budget_range, + /*use_known_optimum*/false); + + logger.set_complete_flag(true); + logger.set_interval(0); + logger.activate_logger(); + + /***** IOH problem *****/ + double w_model_suite_dummy_para = problem_config_mapping[problem].dummy; + int w_model_suite_epitasis_para = problem_config_mapping[problem].epistasis; + int w_model_suite_neutrality_para = problem_config_mapping[problem].neutrality; + int w_model_suite_ruggedness_para = problem_config_mapping[problem].ruggedness; + + W_Model_OneMax w_model_om; + std::string problem_name = "OneMax"; + problem_name = problem_name + + "_D" + std::to_string((int)(w_model_suite_dummy_para * dimension)) + + "_E" + std::to_string(w_model_suite_epitasis_para) + + "_N" + std::to_string(w_model_suite_neutrality_para) + + "_R" + std::to_string(w_model_suite_ruggedness_para); + + + /// This must be called to configure the w-model to be tested. + w_model_om.set_w_setting(w_model_suite_dummy_para,w_model_suite_epitasis_para, + w_model_suite_neutrality_para,w_model_suite_ruggedness_para); + + /// Set problem_name based on the configuration. + w_model_om.IOHprofiler_set_problem_name(problem_name); + + /// Set problem_id as 1 + w_model_om.IOHprofiler_set_problem_id(problem); // FIXME check what that means + // w_model_om.IOHprofiler_set_instance_id(instance); // FIXME changing the instance seems to change the target upper bound. + + /// Set dimension. + w_model_om.IOHprofiler_set_number_of_variables(dimension); + + /***** Bindings *****/ + logger.track_problem(w_model_om); + + eoEvalIOHproblem onemax_pb(w_model_om, logger); + + // eoEvalPrint eval_print(onemax_pb, std::clog, "\n"); + eoEvalFuncCounter eval_count(onemax_pb); + + eoPopLoopEval onemax_eval(eval_count); + + /***** Instanciate and run the algo *****/ + + eoBooleanGenerator bgen; + eoInitFixedLength onemax_init(/*bitstring size=*/dimension, bgen); + auto& foundry = make_foundry(store, onemax_init, eval_count, max_evals - pop_size, generations); + + Ints encoded_algo(foundry.size()); + + encoded_algo[foundry.crossover_rates .index()] = crossover_rate; + encoded_algo[foundry.crossover_selectors .index()] = crossover_selector; + encoded_algo[foundry.crossovers .index()] = crossover; + encoded_algo[foundry.aftercross_selectors.index()] = aftercross_selector; + encoded_algo[foundry.mutation_rates .index()] = mutation_rate; + encoded_algo[foundry.mutation_selectors .index()] = mutation_selector; + encoded_algo[foundry.mutations .index()] = mutation; + encoded_algo[foundry.replacements .index()] = replacement; + encoded_algo[foundry.continuators .index()] = continuator; + encoded_algo[foundry.offspring_sizes .index()] = offspring_size; + + std::clog << "Encoded algorithm:" << std::endl; + foundry.select(encoded_algo); + std::clog << foundry.name() << std::endl; + + // // Evaluation of a forged encoded_algo on the sub-problem + // eoEvalFoundryFastGA eval_foundry( + // foundry, pop_size, + // onemax_init, onemax_eval, + // /*penalization=*/ dimension, // Worst case penalization. + // /*normalized=*/ false); // Use direct integer encoding. + // + // // Actually instanciate and run the algorithm. + // eval_foundry(encoded_algo); + + eoPop pop; + pop.append(pop_size, onemax_init); + onemax_eval(pop,pop); + foundry(pop); // Actually run the selected algorithm. + + /***** IOH perf stats *****/ + IOHprofiler_ecdf_sum ecdf_sum; + // iRace expects minimization + long perf = ecdf_sum(logger.data()); + + // assert(0 < perf and perf <= buckets*buckets); + if(perf <= 0 or buckets*buckets < perf) { + std::cerr << "WARNING: illogical performance: " << perf + << ", check the bounds or the algorithm." << std::endl; + } + + // std::clog << "After " << eval_count.getValue() << " / " << max_evals << " evaluations" << std::endl; + + // Output + std::cout << -1 * perf << std::endl; + +} diff --git a/eo/contrib/irace/irace-algo-search/target-runner b/eo/contrib/irace/irace-algo-search/target-runner new file mode 100755 index 000000000..f9b0e3198 --- /dev/null +++ b/eo/contrib/irace/irace-algo-search/target-runner @@ -0,0 +1,87 @@ +#!/bin/bash +############################################################################### +# This script is the command that is executed every run. +# Check the examples in examples/ +# +# This script is run in the execution directory (execDir, --exec-dir). +# +# PARAMETERS: +# $1 is the candidate configuration number +# $2 is the instance ID +# $3 is the seed +# $4 is the instance name +# The rest ($* after `shift 4') are parameters to the run +# +# RETURN VALUE: +# This script should print one numerical value: the cost that must be minimized. +# Exit with 0 if no error, with 1 in case of error +############################################################################### +error() { + echo "`TZ=UTC date`: $0: error: $@" + exit 1 +} + + +EXE="/home/aaziz-alaoui/Documents/GitHub/paradiseo/eo/contrib/irace/irace-algo-search/bin/fastga" + +FIXED_PARAMS="" + +CONFIG_ID=$1 +INSTANCE_ID=$2 +SEED=$3 +INSTANCE=$4 +CROSSOVER_RATE=$5 +CROSSOVER_SELECTOR=$6 +CROSSOVER=$7 +MUTATION_RATE=$8 +MUT_SELECTOR=$9 +MUTATION=${10} +REPLACEMENT=${11} +shift 11 || error "Not enough parameters" + +CONFIG_PARAMS=$* + +STDOUT=c${CONFIG_ID}-${INSTANCE_ID}-${SEED}.stdout +STDERR=c${CONFIG_ID}-${INSTANCE_ID}-${SEED}.stderr + +if [ ! -x "${EXE}" ]; then + error "${EXE}: not found or not executable (pwd: $(pwd))" +fi + +# If the program just prints a number, we can use 'exec' to avoid +# creating another process, but there can be no other commands after exec. +#exec $EXE ${FIXED_PARAMS} -i $INSTANCE ${CONFIG_PARAMS} +# exit 1 +# +# Otherwise, save the output to a file, and parse the result from it. +# (If you wish to ignore segmentation faults you can use '{}' around +# the command.) +$EXE ${FIXED_PARAMS} --instance=$INSTANCE --seed=${SEED} --crossover-rate=${CROSSOVER_RATE} --cross-selector=${CROSSOVER_SELECTOR} --crossover=${CROSSOVER} --mutation-rate=${MUTATION_RATE} --mut-selector=${MUT_SELECTOR} --mutation=${MUTATION} --replacement=${REPLACEMENT} 1> ${STDOUT} 2> ${STDERR} + +#echo ${cmd} + + +# --instance=$INSTANCE --seed=$SEED +# remplacer config param par la même config que seed et instance id + + + +# This may be used to introduce a delay if there are filesystem +# issues. +#SLEEPTIME=1 +#while [ ! -s "${STDOUT}" ]; do +# sleep $SLEEPTIME +# let "SLEEPTIME += 1" +#done + +# This is an example of reading a number from the output. +# It assumes that the objective value is the first number in +# the first column of the last line of the output. +if [ -s "${STDOUT}" ]; then + COST=$(tail -n 1 ${STDOUT} | grep -e '^[[:space:]]*[+-]\?[0-9]' | cut -f1) + echo "$COST" + rm -f "${STDOUT}" "${STDERR}" + exit 0 +else + error "${STDOUT}: No such file or directory" +fi diff --git a/eo/contrib/irace/irace-config/default.instances b/eo/contrib/irace/irace-config/default.instances new file mode 100644 index 000000000..a0a1adfc3 --- /dev/null +++ b/eo/contrib/irace/irace-config/default.instances @@ -0,0 +1,48 @@ +## This is an example of specifying instances with a file. + +# Each line is an instance relative to trainInstancesDir +# (see scenario.txt.tmpl) and an optional sequence of instance-specific +# parameters that will be passed to target-runnerx when invoked on that +# instance. + +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 diff --git a/eo/contrib/irace/irace-config/example.scen b/eo/contrib/irace/irace-config/example.scen new file mode 100644 index 000000000..6c7abb972 --- /dev/null +++ b/eo/contrib/irace/irace-config/example.scen @@ -0,0 +1,227 @@ +###################################################### -*- mode: r -*- ##### +## Scenario setup for Iterated Race (irace). +############################################################################ + +## To use the default value of a parameter of iRace, simply do not set +## the parameter (comment it out in this file, and do not give any +## value on the command line). + +## File that contains the description of the parameters of the target +## algorithm. +parameterFile = "./fastga.param" + +## Directory where the programs will be run. +execDir = "." + +## File to save tuning results as an R dataset, either absolute path or +## relative to execDir. +# logFile = "./irace.Rdata" + +## Previously saved log file to recover the execution of irace, either +## absolute path or relative to the current directory. If empty or NULL, +## recovery is not performed. +# recoveryFile = "" + +## Directory where training instances are located; either absolute path or +## relative to current directory. If no trainInstancesFiles is provided, +## all the files in trainInstancesDir will be listed as instances. +trainInstancesDir = "." + +## File that contains a list of training instances and optionally +## additional parameters for them. If trainInstancesDir is provided, irace +## will search for the files in this folder. +trainInstancesFile = "./default.instances" + +## File that contains a table of initial configurations. If empty or NULL, +## all initial configurations are randomly generated. +# configurationsFile = "" + +## File that contains a list of logical expressions that cannot be TRUE +## for any evaluated configuration. If empty or NULL, do not use forbidden +## expressions. +# forbiddenFile = "" + +## Script called for each configuration that executes the target algorithm +## to be tuned. See templates. +targetRunner = "./target-runner" + +## Number of times to retry a call to targetRunner if the call failed. +# targetRunnerRetries = 0 + +## Optional data passed to targetRunner. This is ignored by the default +## targetRunner function, but it may be used by custom targetRunner +## functions to pass persistent data around. +# targetRunnerData = "" + +## Optional R function to provide custom parallelization of targetRunner. +# targetRunnerParallel = "" + +## Optional script or R function that provides a numeric value for each +## configuration. See templates/target-evaluator.tmpl +# targetEvaluator = "" + +## Maximum number of runs (invocations of targetRunner) that will be +## performed. It determines the maximum budget of experiments for the +## tuning. +maxExperiments = 2000 + +## Maximum total execution time in seconds for the executions of +## targetRunner. targetRunner must return two values: cost and time. +# maxTime = 60 + +## Fraction (smaller than 1) of the budget used to estimate the mean +## computation time of a configuration. Only used when maxTime > 0 +# budgetEstimation = 0.02 + +## Maximum number of decimal places that are significant for numerical +## (real) parameters. +digits = 2 + +## Debug level of the output of irace. Set this to 0 to silence all debug +## messages. Higher values provide more verbose debug messages. +# debugLevel = 0 + +## Number of iterations. +# nbIterations = 0 + +## Number of runs of the target algorithm per iteration. +# nbExperimentsPerIteration = 0 + +## Randomly sample the training instances or use them in the order given. +# sampleInstances = 1 + +## Statistical test used for elimination. Default test is always F-test +## unless capping is enabled, in which case the default test is t-test. +## Valid values are: F-test (Friedman test), t-test (pairwise t-tests with +## no correction), t-test-bonferroni (t-test with Bonferroni's correction +## for multiple comparisons), t-test-holm (t-test with Holm's correction +## for multiple comparisons). +# testType = "F-test" + +## Number of instances evaluated before the first elimination test. It +## must be a multiple of eachTest. +# firstTest = 5 + +## Number of instances evaluated between elimination tests. +# eachTest = 1 + +## Minimum number of configurations needed to continue the execution of +## each race (iteration). +# minNbSurvival = 0 + +## Number of configurations to be sampled and evaluated at each iteration. +# nbConfigurations = 0 + +## Parameter used to define the number of configurations sampled and +## evaluated at each iteration. +# mu = 5 + +## Confidence level for the elimination test. +# confidence = 0.95 + +## If the target algorithm is deterministic, configurations will be +## evaluated only once per instance. +# deterministic = 0 + +## Seed of the random number generator (by default, generate a random +## seed). +# seed = NA + +## Number of calls to targetRunner to execute in parallel. Values 0 or 1 +## mean no parallelization. +# parallel = 0 + +## Enable/disable load-balancing when executing experiments in parallel. +## Load-balancing makes better use of computing resources, but increases +## communication overhead. If this overhead is large, disabling +## load-balancing may be faster. +# loadBalancing = 1 + +## Enable/disable MPI. Use Rmpi to execute targetRunner in parallel +## (parameter parallel is the number of slaves). +# mpi = 0 + +## Specify how irace waits for jobs to finish when targetRunner submits +## jobs to a batch cluster: sge, pbs, torque or slurm. targetRunner must +## submit jobs to the cluster using, for example, qsub. +# batchmode = 0 + +## Enable/disable the soft restart strategy that avoids premature +## convergence of the probabilistic model. +# softRestart = 1 + +## Soft restart threshold value for numerical parameters. If NA, NULL or +## "", it is computed as 10^-digits. +# softRestartThreshold = "" + +## Directory where testing instances are located, either absolute or +## relative to current directory. +# testInstancesDir = "" + +## File containing a list of test instances and optionally additional +## parameters for them. +# testInstancesFile = "" + +## Number of elite configurations returned by irace that will be tested if +## test instances are provided. +# testNbElites = 1 + +## Enable/disable testing the elite configurations found at each +## iteration. +# testIterationElites = 0 + +## Enable/disable elitist irace. +# elitist = 1 + +## Number of instances added to the execution list before previous +## instances in elitist irace. +# elitistNewInstances = 1 + +## In elitist irace, maximum number per race of elimination tests that do +## not eliminate a configuration. Use 0 for no limit. +# elitistLimit = 2 + +## User-defined R function that takes a configuration generated by irace +## and repairs it. +# repairConfiguration = "" + +## Enable the use of adaptive capping, a technique designed for minimizing +## the computation time of configurations. This is only available when +## elitist is active. +# capping = 0 + +## Measure used to obtain the execution bound from the performance of the +## elite configurations: median, mean, worst, best. +# cappingType = "median" + +## Method to calculate the mean performance of elite configurations: +## candidate or instance. +# boundType = "candidate" + +## Maximum execution bound for targetRunner. It must be specified when +## capping is enabled. +# boundMax = 0 + +## Precision used for calculating the execution time. It must be specified +## when capping is enabled. +# boundDigits = 0 + +## Penalization constant for timed out executions (executions that reach +## boundMax execution time). +# boundPar = 1 + +## Replace the configuration cost of bounded executions with boundMax. +# boundAsTimeout = 1 + +## Percentage of the configuration budget used to perform a postselection +## race of the best configurations of each iteration after the execution +## of irace. +# postselection = 0 + +## Enable/disable AClib mode. This option enables compatibility with +## GenericWrapper4AC as targetRunner script. +# aclib = 0 + +## END of scenario file +############################################################################ + diff --git a/eo/contrib/irace/irace-config/target-runner b/eo/contrib/irace/irace-config/target-runner new file mode 100755 index 000000000..6d6e39dd7 --- /dev/null +++ b/eo/contrib/irace/irace-config/target-runner @@ -0,0 +1,87 @@ +#!/bin/bash +############################################################################### +# This script is the command that is executed every run. +# Check the examples in examples/ +# +# This script is run in the execution directory (execDir, --exec-dir). +# +# PARAMETERS: +# $1 is the candidate configuration number +# $2 is the instance ID +# $3 is the seed +# $4 is the instance name +# The rest ($* after `shift 4') are parameters to the run +# +# RETURN VALUE: +# This script should print one numerical value: the cost that must be minimized. +# Exit with 0 if no error, with 1 in case of error +############################################################################### +error() { + echo "`TZ=UTC date`: $0: error: $@" + exit 1 +} + + +EXE="./fastga" +LOG_DIR="irace_logs" + +FIXED_PARAMS="--problem=0" + +CONFIG_ID=$1 +INSTANCE_ID=$2 +SEED=$3 +INSTANCE=$(echo $4 | sed 's/\//\n/g'|tail -n 1) +CROSSOVER_RATE=$5 +CROSSOVER_SELECTOR=$6 +CROSSOVER=$7 +MUTATION_RATE=$8 +MUT_SELECTOR=$9 +MUTATION=${10} +REPLACEMENT=${11} +shift 11 || error "Not enough parameters" + +INSTANCE_PARAMS=$* + +# STDOUT=${LOG_DIR}/c${CONFIG_ID}_i${INSTANCE_ID}_s${SEED}.stdout +# STDERR=${LOG_DIR}/c${CONFIG_ID}_i${INSTANCE_ID}_s${SEED}.stderr +STDOUT="/dev/null" +STDERR="/dev/null" + +if [ ! -x "${EXE}" ]; then + error "${EXE}: not found or not executable (pwd: $(pwd))" +fi + +# If the program just prints a number, we can use 'exec' to avoid +# creating another process, but there can be no other commands after exec. +#exec $EXE ${FIXED_PARAMS} -i $INSTANCE ${INSTANCE_PARAMS} +# exit 1 +# +# Otherwise, save the output to a file, and parse the result from it. +# (If you wish to ignore segmentation faults you can use '{}' around +# the command.) +cmd="$EXE ${FIXED_PARAMS} --instance=${INSTANCE} --seed=${SEED} ${CROSSOVER_RATE} ${CROSSOVER_SELECTOR} ${CROSSOVER} ${MUTATION_RATE} ${MUT_SELECTOR} ${MUTATION} ${REPLACEMENT}" +# NOTE: irace seems to capture both stderr and stdout, so you should not output to stderr +echo ${cmd} > ${STDERR} +$cmd 2> ${STDERR} | tee ${STDOUT} + +# The following code is useless if the binary only output a single number on stdout. + +# This may be used to introduce a delay if there are filesystem +# issues. +# SLEEPTIME=1 +# while [ ! -s "${STDOUT}" ]; do +# sleep $SLEEPTIME +# let "SLEEPTIME += 1" +# done + +# This is an example of reading a number from the output. +# It assumes that the objective value is the first number in +# the first column of the last line of the output. +# if [ -s "${STDOUT}" ]; then +# COST=$(tail -n 1 ${STDOUT} | grep -e '^[[:space:]]*[+-]\?[0-9]' | cut -f1) +# echo "$COST" +# rm -f "${STDOUT}" "${STDERR}" +# exit 0 +# else +# error "${STDOUT}: No such file or directory" +# fi diff --git a/eo/contrib/irace/run_irace.sh b/eo/contrib/irace/run_irace.sh new file mode 100755 index 000000000..17c79e88d --- /dev/null +++ b/eo/contrib/irace/run_irace.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +if [[ $# != 1 ]] ; then + echo "ERROR: build dir not indicated" + exit 1 +fi + +cd $1 +pwd + +# Fore some reason, irace absolutely need those files... +cp ../irace-config/example.scen . +cp ../irace-config/target-runner . +cp ../irace-config/default.instances . + +# Generate the parameter list file. +./fastga -h > fastga.param 2>/dev/null +/usr/lib/R/site-library/irace/bin/irace --scenario example.scen 2>&1 | tee irace.log + diff --git a/eo/src/eo b/eo/src/eo index a296c7091..55c070857 100644 --- a/eo/src/eo +++ b/eo/src/eo @@ -85,6 +85,7 @@ #include "eoEvalKeepBest.h" #include "eoEvalTimeThrowException.h" #include "eoEvalUserTimeThrowException.h" +#include "eoEvalPrint.h" // Continuators - all include eoContinue.h #include "eoCombinedContinue.h" @@ -146,6 +147,7 @@ // Algorithms #include "eoEasyEA.h" #include "eoSGA.h" +#include "eoFastGA.h" // #include "eoEvolutionStrategy.h" removed for a while - until eoGenOp is done #include "eoAlgoReset.h" #include "eoAlgoRestart.h" @@ -164,6 +166,7 @@ #include "eoAlgoFoundryEA.h" #include "eoAlgoFoundryFastGA.h" #include "eoEvalFoundryEA.h" +#include "eoEvalFoundryFastGA.h" //----------------------------------------------------------------------------- // to be continued ... diff --git a/eo/src/eoAlgoFoundry.h b/eo/src/eoAlgoFoundry.h index 0efa18a6d..19ffb5023 100644 --- a/eo/src/eoAlgoFoundry.h +++ b/eo/src/eoAlgoFoundry.h @@ -76,12 +76,12 @@ class eoOperatorFoundry : public eoForgeVector< Itf > * In a second step, the operators to be used should be selected * by indicating their index, just like if the foundry was an array: * @code - * foundry = {0, 1, 2}; - * // ^ ^ ^ - * // | | | - * // | | + 3d operator - * // | + 2d operator - * // + 1st operator + * foundry.select({0, 1, 2}); + * // ^ ^ ^ + * // | | | + * // | | + 3d operator + * // | + 2d operator + * // + 1st operator * @endcode * * If you don't (want to) recall the order of the operators in the encoding, diff --git a/eo/src/eoAlgoFoundryFastGA.h b/eo/src/eoAlgoFoundryFastGA.h index f9ab98f00..ed766ff88 100644 --- a/eo/src/eoAlgoFoundryFastGA.h +++ b/eo/src/eoAlgoFoundryFastGA.h @@ -27,30 +27,30 @@ #include #include -/** A class that assemble an eoEasyEA on the fly, given a combination of available operators. +/** A class that assemble an eoFastGA on the fly, given a combination of available operators. * * The foundry should first be set up with sets of operators - * for the main modules of an EA: - * continuators, crossovers, mutations, selection and replacement operators. + * for the main modules of a FastGA: + * continuators, crossovers, mutations, selections, replacement operators, etc. * * This is done through public member variable's `add` method, * which takes the class name as template and its constructor's parameters * as arguments. For example: * @code - * foundry.selectors.add< eoStochTournamentSelect >( 0.5 ); + * foundry.selectors.add< eoRandomSelect >(); * @endcode * * @warning If the constructor takes a reference YOU SHOULD ABSOLUTELY wrap it * in a `std::ref`, or it will silently be passed as a copy, - * which would effectively disable any link between operators. + * which would effectively disable any link with other operator(s). * * In a second step, the operators to be used should be selected - * by indicating their index, passing an array of eight elements: + * by indicating their index, passing an array of 10 elements: * @code - * foundry.select({0, 1, 2, 3, 4, 5, 6, 7}); + * foundry.select({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); * @endcode * - * @note: by default, the firsts of the eight operators are selected. + * @note: by default, the firsts of the 10 operators are selected. * * If you don't (want to) recall the order of the operators in the encoding, * you can use the `index()` member, for example: @@ -58,7 +58,7 @@ * foundry.at(foundry.continuators.index()) = 2; // select the third continuator * @endcode * - * Now, you can call the fourdry just like any eoAlgo, by passing it an eoPop: + * Now, you can call the foundry just like any eoAlgo, by passing it an eoPop: * @code * foundry(pop); * @encode @@ -69,7 +69,7 @@ * Every instantiation is deferred upon actual use. That way, you can still reconfigure them * at any time with `eoForgeOperator::setup`, for example: * @code - * foundry.selector.at(0).setup(0.5); // using constructor's arguments + * foundry.selector.at(0).setup(0.5); // Will call constructor's arguments * @endcode * * @ingroup Foundry @@ -82,16 +82,26 @@ class eoAlgoFoundryFastGA : public eoAlgoFoundry /** The constructon only take an eval, because all other operators * are stored in the public containers. */ - eoAlgoFoundryFastGA( eoInit & init, eoEvalFunc& eval, size_t max_evals = 10000, size_t max_restarts = std::numeric_limits::max() ) : - eoAlgoFoundry(8), - continuators(0, true), // Always re-instantiate continuators, because they hold a state. - crossover_rates(1, false), + eoAlgoFoundryFastGA( + eoInit & init, + eoEvalFunc& eval, + size_t max_evals = 10000, + size_t max_restarts = std::numeric_limits::max() + ) : + eoAlgoFoundry(10), + + crossover_rates(0, false), + crossover_selectors(1, false), crossovers(2, false), - mutation_rates(3, false), - mutations(4, false), - selectors(5, false), - pop_sizes(6, false), + aftercross_selectors(3, false), + + mutation_rates(4, false), + mutation_selectors(5, false), + mutations(6, false), + replacements(7, false), + continuators(8, true), // Always re-instantiate continuators, because they hold a state. + offspring_sizes(9, false), _eval(eval), _init(init), _max_evals(max_evals), @@ -101,93 +111,89 @@ class eoAlgoFoundryFastGA : public eoAlgoFoundry public: /* Operators containers @{ */ - eoOperatorFoundry< eoContinue > continuators; eoOperatorFoundry< double > crossover_rates; + eoOperatorFoundry< eoSelectOne > crossover_selectors; eoOperatorFoundry< eoQuadOp > crossovers; + eoOperatorFoundry< eoSelectOne > aftercross_selectors; + eoOperatorFoundry< double > mutation_rates; + eoOperatorFoundry< eoSelectOne > mutation_selectors; eoOperatorFoundry< eoMonOp > mutations; - eoOperatorFoundry< eoSelectOne > selectors; - eoOperatorFoundry< size_t > pop_sizes; + eoOperatorFoundry< eoReplacement > replacements; + eoOperatorFoundry< eoContinue > continuators; + eoOperatorFoundry< size_t > offspring_sizes; /* @} */ /** instantiate and call the pre-selected algorithm. */ void operator()(eoPop& pop) { - assert(continuators.size() > 0); assert(this->at(continuators.index()) < continuators.size()); - assert( crossover_rates.size() > 0); assert(this->at( crossover_rates.index()) < crossover_rates.size()); - assert( crossovers.size() > 0); assert(this->at( crossovers.index()) < crossovers.size()); - assert( mutation_rates.size() > 0); assert(this->at( mutation_rates.index()) < mutation_rates.size()); - assert( mutations.size() > 0); assert(this->at( mutations.index()) < mutations.size()); - assert( selectors.size() > 0); assert(this->at( selectors.index()) < selectors.size()); - assert( pop_sizes.size() > 0); assert(this->at( pop_sizes.index()) < pop_sizes.size()); - assert(replacements.size() > 0); assert(this->at(replacements.index()) < replacements.size()); - - // Crossover or clone - double cross_rate = this->crossover_rate(); - eoProportionalOp cross; - // Cross-over that produce only one offspring, - // made by wrapping the quad op (which produce 2 offsprings) - // in a bin op (which ignore the second offspring). - eoQuad2BinOp single_cross(this->crossover()); - cross.add(single_cross, cross_rate); - eoBinCloneOp cross_clone; - cross.add(cross_clone, 1 - cross_rate); // Clone - - // Mutation or clone - double mut_rate = this->mutation_rate(); - eoProportionalOp mut; - mut.add(this->mutation(), mut_rate); - eoMonCloneOp mut_clone; - mut.add(mut_clone, 1 - mut_rate); // FIXME TBC - - // Apply mutation after cross-over. - eoSequentialOp variator; - variator.add(cross,1.0); - variator.add(mut,1.0); - - // All variatiors - double lambda = this->pop_size(); - eoGeneralBreeder breeder(this->selector(), variator, lambda, /*as rate*/false); + assert( crossover_rates.size() > 0); assert(this->at( crossover_rates.index()) < crossover_rates.size()); + assert( crossover_selectors.size() > 0); assert(this->at( crossover_selectors.index()) < crossover_selectors.size()); + assert( crossovers.size() > 0); assert(this->at( crossovers.index()) < crossovers.size()); + assert(aftercross_selectors.size() > 0); assert(this->at(aftercross_selectors.index()) < aftercross_selectors.size()); + assert( mutation_rates.size() > 0); assert(this->at( mutation_rates.index()) < mutation_rates.size()); + assert( mutation_selectors.size() > 0); assert(this->at( mutation_selectors.index()) < mutation_selectors.size()); + assert( mutations.size() > 0); assert(this->at( mutations.index()) < mutations.size()); + assert( replacements.size() > 0); assert(this->at( replacements.index()) < replacements.size()); + assert( continuators.size() > 0); assert(this->at( continuators.index()) < continuators.size()); + assert( offspring_sizes.size() > 0); assert(this->at( offspring_sizes.index()) < offspring_sizes.size()); // Objective function calls counter eoEvalCounterThrowException eval(_eval, _max_evals); + eo::log << eo::xdebug << "Evaluations: " << eval.value() << " / " << _max_evals << std::endl; eoPopLoopEval pop_eval(eval); // Algorithm itself - eoEasyEA algo = eoEasyEA(this->continuator(), pop_eval, breeder, this->replacement()); + eoFastGA algo( + this->crossover_rate(), + this->crossover_selector(), + this->crossover(), + this->aftercross_selector(), + this->mutation_rate(), + this->mutation_selector(), + this->mutation(), + pop_eval, + this->replacement(), + this->continuator(), + this->offspring_size() + ); // Restart wrapper - eoAlgoPopReset reset_pop(_init, pop_eval); - eoGenContinue restart_cont(_max_restarts); - eoAlgoRestart restart(eval, algo, restart_cont, reset_pop); + // eoAlgoPopReset reset_pop(_init, pop_eval); + // eoGenContinue restart_cont(_max_restarts); + // eoAlgoRestart restart(eval, algo, restart_cont, reset_pop); try { - restart(pop); + // restart(pop); + algo(pop); } catch(eoMaxEvalException e) { +#ifndef NDEBUG + eo::log << eo::debug << "Reached maximum evaluations: " << eval.getValue() << " / " << _max_evals << std::endl; +#endif // In case some solutions were not evaluated when max eval occured. - eoPopLoopEval pop_last_eval(_eval); - pop_last_eval(pop,pop); + // FIXME can this even be considered legal? + // eoPopLoopEval pop_last_eval(_eval); + // pop_last_eval(pop,pop); } } /** Return an approximate name of the selected algorithm. - * - * @note: does not take into account parameters of the operators, - * only show class names. */ std::string name() { std::ostringstream name; - name << this->at(continuators.index()) << " (" << this->continuator().className() << ") + "; - name << this->at(crossover_rates.index()) << " (" << this->crossover_rate().className() << ") + "; - name << this->at(crossovers.index()) << " (" << this->crossover().className() << ") + "; - name << this->at(mutation_rates.index()) << " (" << this->mutation_rate().className() << ") + "; - name << this->at(mutations.index()) << " (" << this->mutation().className() << ") + "; - name << this->at(selectors.index()) << " (" << this->selector().className() << ") + "; - name << this->at(pop_sizes.index()) << " (" << this->pop_size().className() << ")"; - name << this->at(replacements.index()) << " (" << this->replacement().className() << ")"; + name << "crossover_rates: " << this->at( crossover_rates.index()) << " (" << this-> crossover_rate() << ") + "; + name << "crossover_selectors: " << this->at( crossover_selectors.index()) << " (" << this-> crossover_selector().className() << ") + "; + name << "aftercross_selector: " << this->at(aftercross_selectors.index()) << " (" << this->aftercross_selector().className() << ") + "; + name << "crossovers: " << this->at( crossovers.index()) << " (" << this-> crossover().className() << ") + "; + name << "mutation_rates: " << this->at( mutation_rates.index()) << " (" << this-> mutation_rate() << ") + "; + name << "mutation_selectors: " << this->at( mutation_selectors.index()) << " (" << this-> mutation_selector().className() << ") + "; + name << "mutations: " << this->at( mutations.index()) << " (" << this-> mutation().className() << ") + "; + name << "replacements: " << this->at( replacements.index()) << " (" << this-> replacement().className() << ") + "; + name << "continuators: " << this->at( continuators.index()) << " (" << this-> continuator().className() << ") + "; + name << "offspring_sizes: " << this->at( offspring_sizes.index()) << " (" << this-> offspring_size() << ")"; return name.str(); } @@ -228,16 +234,28 @@ class eoAlgoFoundryFastGA : public eoAlgoFoundry return mutations.instantiate(this->at(mutations.index())); } - eoSelectOne& selector() + eoSelectOne& crossover_selector() { - assert(this->at(selectors.index()) < selectors.size()); - return selectors.instantiate(this->at(selectors.index())); + assert(this->at(crossover_selectors.index()) < crossover_selectors.size()); + return crossover_selectors.instantiate(this->at(crossover_selectors.index())); } - size_t& pop_size() + eoSelectOne& aftercross_selector() { - assert(this->at(pop_sizes.index()) < pop_sizes.size()); - return pop_sizes.instantiate(this->at(pop_sizes.index())); + assert(this->at(aftercross_selectors.index()) < aftercross_selectors.size()); + return aftercross_selectors.instantiate(this->at(aftercross_selectors.index())); + } + + eoSelectOne& mutation_selector() + { + assert(this->at(mutation_selectors.index()) < mutation_selectors.size()); + return mutation_selectors.instantiate(this->at(mutation_selectors.index())); + } + + size_t& offspring_size() + { + assert(this->at(offspring_sizes.index()) < offspring_sizes.size()); + return offspring_sizes.instantiate(this->at(offspring_sizes.index())); } eoReplacement& replacement() diff --git a/eo/src/eoAlgoRestart.h b/eo/src/eoAlgoRestart.h index 2f61ea4e5..ebea3aec5 100644 --- a/eo/src/eoAlgoRestart.h +++ b/eo/src/eoAlgoRestart.h @@ -147,6 +147,9 @@ public: virtual void operator()(eoPop & pop) { do { +#ifndef NDEBUG + eo::log << eo::debug << "Restart" << std::endl; +#endif _reseter(pop); _algo(pop); } while( _continue(pop) ); diff --git a/eo/src/eoEasyEA.h b/eo/src/eoEasyEA.h index fc2ebd6b2..ace02604a 100644 --- a/eo/src/eoEasyEA.h +++ b/eo/src/eoEasyEA.h @@ -272,7 +272,7 @@ template class eoEasyEA: public eoAlgo replace(_pop, offspring); // after replace, the new pop. is in _pop - std::cout << _pop << std::endl; + // std::cout << _pop << std::endl; if (pSize > _pop.size()) throw eoException("Population shrinking!"); diff --git a/eo/src/eoEvalCounterThrowException.h b/eo/src/eoEvalCounterThrowException.h index b83699916..a17982a43 100644 --- a/eo/src/eoEvalCounterThrowException.h +++ b/eo/src/eoEvalCounterThrowException.h @@ -64,20 +64,18 @@ public : // bypass already evaluated individuals if (eo.invalid()) { - // increment the value of the self parameter - // (eoEvalFuncCounter inherits from @see eoValueParam) - value()++; + // evaluate + this->eoEvalFuncCounter::operator()(eo); + // No need to increment value(), it is done in the superclass. + // increment t // if we have reached the maximum - if ( value() >= _threshold ) { + if ( this->value() >= _threshold ) { // go back through the stack until catched throw eoMaxEvalException(_threshold); } - // evaluate - this->eoEvalFuncCounter::operator()(eo); - } // if invalid } diff --git a/eo/src/eoEvalFoundryEA.h b/eo/src/eoEvalFoundryEA.h index f3b33cba5..76521c9b6 100644 --- a/eo/src/eoEvalFoundryEA.h +++ b/eo/src/eoEvalFoundryEA.h @@ -92,12 +92,19 @@ public: */ std::vector decode( const EOT& sol ) const { - // Denormalize - size_t cont = static_cast(std::ceil( sol[i_cont] * _foundry.continuators.size() )); - size_t cros = static_cast(std::ceil( sol[i_cros] * _foundry.crossovers .size() )); - size_t muta = static_cast(std::ceil( sol[i_muta] * _foundry.mutations .size() )); - size_t sele = static_cast(std::ceil( sol[i_sele] * _foundry.selectors .size() )); - size_t repl = static_cast(std::ceil( sol[i_repl] * _foundry.replacements.size() )); + // // Denormalize + // size_t cont = static_cast(std::ceil( sol[i_cont] * _foundry.continuators.size() )); + // size_t cros = static_cast(std::ceil( sol[i_cros] * _foundry.crossovers .size() )); + // size_t muta = static_cast(std::ceil( sol[i_muta] * _foundry.mutations .size() )); + // size_t sele = static_cast(std::ceil( sol[i_sele] * _foundry.selectors .size() )); + // size_t repl = static_cast(std::ceil( sol[i_repl] * _foundry.replacements.size() )); + + // Direct encoding + size_t cont = static_cast(std::ceil( sol[i_cont] )); + size_t cros = static_cast(std::ceil( sol[i_cros] )); + size_t muta = static_cast(std::ceil( sol[i_muta] )); + size_t sele = static_cast(std::ceil( sol[i_sele] )); + size_t repl = static_cast(std::ceil( sol[i_repl] )); return {cont, cros, muta, sele, repl}; } diff --git a/eo/src/eoEvalFoundryFastGA.h b/eo/src/eoEvalFoundryFastGA.h new file mode 100644 index 000000000..349fa6aad --- /dev/null +++ b/eo/src/eoEvalFoundryFastGA.h @@ -0,0 +1,224 @@ + +/* + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + © 2020 Thales group + + Authors: + Johann Dreo +*/ + +#ifndef _eoEvalFoundryFastGA_H_ +#define _eoEvalFoundryFastGA_H_ + +#include "eoEvalFunc.h" +#include "eoAlgoFoundryFastGA.h" +#include "eoInit.h" +#include "eoPopEvalFunc.h" + +/** Evaluate an algorithm assembled by an eoAlgoFoundryFastGA, encoded as a numeric vector. + * + * Allows to plug another search algorithm on top of an eoAlgoFoundryFastGA, + * so as to find the best configuration. + * + * The first template EOT is the encoding of the high-level algorithm selection problem, + * the second template SUB is the encoding of the low-level generic problem. + * + * @ingroup Evaluation + * @ingroup Foundry + */ +template +class eoEvalFoundryFastGA : public eoEvalFunc +{ +public: + /** Takes the necessary parameters to perform a search on the sub-problem. + * + * @param foundry The set of algorithms among which to select. + * @param subpb_init An initilizer for sub-problem encoding. + * @param offspring_size Population size for the sub-problem solver. + * @param subpb_eval The sub-problem itself. + * @param penalization If any solution to the high-level algorithm selection problem is out of bounds, set it to this value. + */ + eoEvalFoundryFastGA( + eoAlgoFoundryFastGA& foundry, + const size_t pop_size, + eoInit& subpb_init, + eoPopEvalFunc& subpb_eval, + const typename SUB::Fitness penalization, + const bool normalized = false + ) : + _pop_size(pop_size), + _subpb_init(subpb_init), + _subpb_eval(subpb_eval), + _foundry(foundry), + _penalization(penalization), + _normalized(normalized), + i_crat(foundry.crossover_rates.index()), + i_crsl(foundry.crossover_selectors.index()), + i_cros(foundry.crossovers.index()), + i_afcr(foundry.aftercross_selectors.index()), + i_mrat(foundry.mutation_rates.index()), + i_musl(foundry.mutation_selectors.index()), + i_muta(foundry.mutations.index()), + i_repl(foundry.replacements.index()), + i_cont(foundry.continuators.index()), + i_offs(foundry.offspring_sizes.index()) + { } + +protected: + const size_t i_crat; + const size_t i_crsl; + const size_t i_cros; + const size_t i_afcr; + const size_t i_mrat; + const size_t i_musl; + const size_t i_muta; + const size_t i_repl; + const size_t i_cont; + const size_t i_offs; + +public: + + /** Decode the high-level problem encoding as an array of indices. + * + * @note: If the EOT is an eoInt, this will be optimized out. + * + * May be useful for getting a solution back into an eoAlgoFoundryFastGA. + * @code + * foundry = eval.decode(pop.best_element()); + * std::cout << foundry.name() << std::endl; + * auto& cont = foundry.continuator(); // Get the configured operator + * @encode + */ + std::vector decode( const EOT& sol ) const + { + size_t crat; + size_t crsl; + size_t cros; + size_t afcr; + size_t mrat; + size_t musl; + size_t muta; + size_t repl; + size_t cont; + size_t offs; + + if(_normalized) { + crat = static_cast(std::ceil( sol[i_crat] * _foundry.crossover_rates.size() )); + crsl = static_cast(std::ceil( sol[i_crsl] * _foundry.crossover_selectors.size() )); + cros = static_cast(std::ceil( sol[i_cros] * _foundry.crossovers.size() )); + afcr = static_cast(std::ceil( sol[i_afcr] * _foundry.aftercross_selectors.size() )); + mrat = static_cast(std::ceil( sol[i_mrat] * _foundry.mutation_rates.size() )); + musl = static_cast(std::ceil( sol[i_musl] * _foundry.mutation_selectors.size() )); + muta = static_cast(std::ceil( sol[i_muta] * _foundry.mutations.size() )); + repl = static_cast(std::ceil( sol[i_repl] * _foundry.replacements.size() )); + cont = static_cast(std::ceil( sol[i_cont] * _foundry.continuators.size() )); + offs = static_cast(std::ceil( sol[i_offs] * _foundry.offspring_sizes.size() )); + + } else { + crat = static_cast(std::ceil( sol[i_crat] )); + crsl = static_cast(std::ceil( sol[i_crsl] )); + cros = static_cast(std::ceil( sol[i_cros] )); + afcr = static_cast(std::ceil( sol[i_afcr] )); + mrat = static_cast(std::ceil( sol[i_mrat] )); + musl = static_cast(std::ceil( sol[i_musl] )); + muta = static_cast(std::ceil( sol[i_muta] )); + repl = static_cast(std::ceil( sol[i_repl] )); + cont = static_cast(std::ceil( sol[i_cont] )); + offs = static_cast(std::ceil( sol[i_offs] )); + } + return {crat, crsl, cros, afcr, mrat, musl, muta, repl, cont, offs}; + } + + /** Perform a sub-problem search with the configuration encoded in the given solution + * and set its (high-level) fitness to the best (low-level) fitness found. + * + * You may want to overload this to perform multiple runs or solve multiple sub-problems. + */ + virtual void operator()(EOT& sol) + { + if(not sol.invalid()) { + return; + } + auto config = decode(sol); + double crat = config[i_crat]; + double crsl = config[i_crsl]; + double cros = config[i_cros]; + double afcr = config[i_afcr]; + double mrat = config[i_mrat]; + double musl = config[i_musl]; + double muta = config[i_muta]; + double repl = config[i_repl]; + double cont = config[i_cont]; + double offs = config[i_offs]; + + if( + 0 <= crat and crat < _foundry.crossover_rates.size() + and 0 <= crsl and crsl < _foundry.crossover_selectors.size() + and 0 <= cros and cros < _foundry.crossovers.size() + and 0 <= afcr and afcr < _foundry.aftercross_selectors.size() + and 0 <= mrat and mrat < _foundry.mutation_rates.size() + and 0 <= musl and musl < _foundry.mutation_selectors.size() + and 0 <= muta and muta < _foundry.mutations.size() + and 0 <= repl and repl < _foundry.replacements.size() + and 0 <= cont and cont < _foundry.continuators.size() + and 0 <= offs and offs < _foundry.offspring_sizes.size() + ) { + _foundry.select(config); + + // Reset pop + eoPop pop; + pop.append( _pop_size, _subpb_init); + _subpb_eval(pop,pop); + + // Actually perform a search + _foundry(pop); + + sol.fitness( pop.best_element().fitness() ); + + } else { + eo::log << eo::warnings << "WARNING: encoded algo is out of bounds, penalize to: " << _penalization << std::endl; + sol.fitness( _penalization ); // penalization + } + } + +protected: + const size_t _pop_size; + eoInit& _subpb_init; + eoPopEvalFunc& _subpb_eval; + eoAlgoFoundryFastGA& _foundry; + const typename EOT::Fitness _penalization; + const bool _normalized; +}; + +/** Helper function to instanciate an eoEvalFoundryFastGA without having to indicate the template for the sub-problem encoding. + * + * The template is deduced from the constructor's parameters. + * Not sure it's more concise than a classical instanciation… + */ +template +eoEvalFoundryFastGA& + make_eoEvalFoundryFastGA( + eoInit& subpb_init, + eoPopEvalFunc& subpb_eval, + eoAlgoFoundryFastGA& foundry, + const typename SUB::Fitness penalization, + const bool normalized = false ) +{ + return *(new eoEvalFoundryFastGA(subpb_init, subpb_eval, foundry, penalization, normalized)); +} + +#endif // _eoEvalFoundryFastGA_H_ + diff --git a/eo/src/eoEvalFuncCounter.h b/eo/src/eoEvalFuncCounter.h index 0641250fa..a30e6e088 100644 --- a/eo/src/eoEvalFuncCounter.h +++ b/eo/src/eoEvalFuncCounter.h @@ -45,8 +45,9 @@ template class eoEvalFuncCounter : public eoEvalFunc, public eoV { if (_eo.invalid()) { - value()++; func(_eo); + value()++; + eo::log << eo::xdebug << "eoEvalFuncCounter: " << value() << std::endl; } } diff --git a/eo/src/eoEvalPrint.h b/eo/src/eoEvalPrint.h new file mode 100644 index 000000000..7b151b8fc --- /dev/null +++ b/eo/src/eoEvalPrint.h @@ -0,0 +1,23 @@ + +template< class EOT> +class eoEvalPrint: public eoEvalFunc +{ + protected: + std::ostream& _out; + eoEvalFunc& _eval; + std::string _sep; + + public: + + eoEvalPrint(eoEvalFunc& eval, std::ostream& out=std::cout, std::string sep="\n") : + _out(out), + _eval(eval), + _sep(sep) + {} + + void operator()( EOT& sol ) + { + _eval(sol); + _out << sol << _sep; + } +}; diff --git a/eo/src/eoFastGA.h b/eo/src/eoFastGA.h new file mode 100644 index 000000000..f157377f1 --- /dev/null +++ b/eo/src/eoFastGA.h @@ -0,0 +1,159 @@ + +/* + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _eoFastGA_H_ +#define _eoFastGA_H_ + +/** The Fast Genetic Algorithm. + * + * @ingroup Algorithms + */ +template +class eoFastGA : public eoAlgo +{ +protected: + double _rate_crossover; + eoSelectOne& _select_cross; + eoQuadOp& _crossover; + eoSelectOne& _select_aftercross; + + double _rate_mutation; + eoSelectOne& _select_mut; + eoMonOp& _mutation; + + eoPopEvalFunc& _pop_eval; + eoReplacement& _replace; + + eoContinue& _continuator; + + double _offsprings_size; + +public: + + eoFastGA( + double rate_crossover, + eoSelectOne& select_cross, + eoQuadOp& crossover, + eoSelectOne& select_aftercross, + double rate_mutation, + eoSelectOne& select_mut, + eoMonOp& mutation, + eoPopEvalFunc& pop_eval, + eoReplacement& replace, + eoContinue& continuator, + double offsprings_size = 0 + ) : + _rate_crossover(rate_crossover), + _select_cross(select_cross), + _crossover(crossover), + _select_aftercross(select_aftercross), + _rate_mutation(rate_mutation), + _select_mut(select_mut), + _mutation(mutation), + _pop_eval(pop_eval), + _replace(replace), + _continuator(continuator), + _offsprings_size(offsprings_size) + { + } + + void operator()(eoPop& pop) + { +#ifndef NDEBUG + assert(pop.size() > 0); + for(auto sol : pop) { + assert(not sol.invalid()); + } +#endif + // Set lambda to the pop size + // if it was not set up at construction. + if(_offsprings_size == 0) { + // eo::log << eo::debug << "Set offspring size to: " << pop.size() << std::endl; + _offsprings_size = pop.size(); + } + + do { + eoPop offsprings; + + for(size_t i=0; i < _offsprings_size; ++i) { + // eo::log << eo::xdebug << "\tOffspring #" << i << std::endl; + + if(eo::rng.flip(_rate_crossover)) { + // eo::log << eo::xdebug << "\t\tDo crossover" << std::endl; + // Manual setup of eoSelectOne + // (usually they are setup in a + // wrapping eoSelect). + _select_cross.setup(pop); + + // Copy of const ref solutions, + // because one alter them hereafter. + EOT sol1 = _select_cross(pop); + EOT sol2 = _select_cross(pop); + + // If the operator returns true, + // solutions have been altered. + if(_crossover(sol1, sol2)) { + sol1.invalidate(); + sol2.invalidate(); + } + + // Select one of the two solutions + // which have been crossed. + eoPop crossed; + crossed.push_back(sol1); + crossed.push_back(sol2); + _select_aftercross.setup(crossed); + EOT sol3 = _select_aftercross(crossed); + + // Additional mutation (X)OR the crossed/cloned solution. + if(eo::rng.flip(_rate_mutation)) { + // eo::log << eo::xdebug << "\t\tDo mutation" << std::endl; + if(_mutation(sol3)) { + sol3.invalidate(); + } + } + offsprings.push_back(sol3); + + } else { // If not crossing, always mutate. + // eo::log << eo::xdebug << "\t\tNo crossover, do mutation" << std::endl; + _select_mut.setup(pop); + EOT sol3 = _select_mut(pop); + if(_mutation(sol3)) { + sol3.invalidate(); + } + offsprings.push_back(sol3); + } + } + assert(offsprings.size() == _offsprings_size); + + _pop_eval(pop, offsprings); + _replace(pop, offsprings); + + // eo::log << eo::xdebug << "\tEnd of generation" << std::endl; + + } while(_continuator(pop)); +#ifndef NDEBUG + assert(pop.size() > 0); + for(auto sol : pop) { + assert(not sol.invalid()); + } +#endif + } + +}; + +#endif // _eoFastGA_H_ diff --git a/eo/src/eoInt.h b/eo/src/eoInt.h index 3975743e8..0f0feb3e1 100644 --- a/eo/src/eoInt.h +++ b/eo/src/eoInt.h @@ -36,7 +36,7 @@ * * @ingroup Representations */ -template class eoInt: public eoVector +template class eoInt: public eoVector { public: @@ -45,14 +45,14 @@ template class eoInt: public eoVector * @param size Size of the std::vector * @param value fill the vector with this value */ - eoInt(unsigned size = 0, int value = 0) : - eoVector(size, value) + eoInt(unsigned size = 0, T value = 0) : + eoVector(size, value) {} /** Constructor copying from a vector (or an initialization list). */ - eoInt(std::vector vec) : - eoVector(vec) + eoInt(std::vector vec) : + eoVector(vec) {} /// My class name. diff --git a/eo/src/eoProportionalSelect.h b/eo/src/eoProportionalSelect.h index 1aa74a35b..7f10d53dc 100644 --- a/eo/src/eoProportionalSelect.h +++ b/eo/src/eoProportionalSelect.h @@ -34,6 +34,7 @@ #include "utils/eoRNG.h" #include "utils/selectors.h" +#include "utils/eoLogger.h" #include "eoSelectOne.h" #include "eoPop.h" @@ -92,9 +93,9 @@ public: typename FitVec::iterator result = std::upper_bound(cumulative.begin(), cumulative.end(), fortune); - assert(fortune <= cumulative.back()); + // assert(fortune <= cumulative.back()); - if(result - cumulative.begin() == _pop.size()) { + if(result - cumulative.begin() >= _pop.size()) { return _pop.back(); } else { return _pop[result - cumulative.begin()]; diff --git a/eo/src/eoRandomSelect.h b/eo/src/eoRandomSelect.h index 1b15bbffa..4fd4dbb18 100644 --- a/eo/src/eoRandomSelect.h +++ b/eo/src/eoRandomSelect.h @@ -48,6 +48,9 @@ template class eoRandomSelect: public eoSelectOne { return _pop[eo::rng.random(_pop.size())] ; } + + virtual std::string className() const {return "eoRandomSelect";} + }; /** eoBestSelect: a selection method that always return the best @@ -64,6 +67,8 @@ template class eoBestSelect: public eoSelectOne { return _pop.best_element() ; } + + virtual std::string className() const {return "eoBestSelect";} }; /** eoNoSelect: returns all individual in order WITHOUT USING FITNESS!!! @@ -86,6 +91,9 @@ template class eoNoSelect: public eoSelectOne current++; return _pop[current-1] ; } + + virtual std::string className() const {return "eoNoSelect";} + private: unsigned current; }; diff --git a/eo/src/eoScalarFitness.h b/eo/src/eoScalarFitness.h index 090589446..b31b6acbb 100644 --- a/eo/src/eoScalarFitness.h +++ b/eo/src/eoScalarFitness.h @@ -112,9 +112,14 @@ comparing with less is the default behaviour. // typedef eoScalarFitness > eoMinimizingFitness; // #endif -using eoMaximizingFitness = eoScalarFitness >; -using eoMinimizingFitness = eoScalarFitness >; +template +using eoMaximizingFitnessT = eoScalarFitness >; +template +using eoMinimizingFitnessT = eoScalarFitness >; + +using eoMaximizingFitness = eoMaximizingFitnessT; +using eoMinimizingFitness = eoMinimizingFitnessT; template std::ostream& operator<<(std::ostream& os, const eoScalarFitness& f) diff --git a/eo/src/ga/eoBitOp.h b/eo/src/ga/eoBitOp.h index 8645a86e1..b3e33efec 100644 --- a/eo/src/ga/eoBitOp.h +++ b/eo/src/ga/eoBitOp.h @@ -110,10 +110,8 @@ class eoDetSingleBitFlip: public eoMonOp * (Default) Constructor. * @param _num_bit The number of bits to change * default is one - equivalent to eoOneBitFlip then - * - * @note: use a reference for num_bit, thus you may change and recall without having to re-instantiate. */ - eoDetSingleBitFlip(const unsigned& _num_bit = 1): num_bit(_num_bit) {} + eoDetSingleBitFlip(const unsigned _num_bit = 1): num_bit(_num_bit) {} /// The class name. virtual std::string className() const { return "eoDetSingleBitFlip"; } @@ -138,6 +136,7 @@ class eoDetSingleBitFlip: public eoMonOp } // Flip at first indices + assert(num_bit <= chrom.size()); for(unsigned i=0; i } } + void number_bits(const unsigned _num_bit) + { + num_bit = _num_bit; + } + protected: - const unsigned& num_bit; + unsigned num_bit; }; diff --git a/eo/src/ga/eoStandardBitMutation.h b/eo/src/ga/eoStandardBitMutation.h index 50ad7873e..a2f7f12aa 100644 --- a/eo/src/ga/eoStandardBitMutation.h +++ b/eo/src/ga/eoStandardBitMutation.h @@ -28,6 +28,8 @@ class eoStandardBitMutation : public eoMonOp return _bitflip(chrom); } + virtual std::string className() const {return "eoStandardBitMutation";} + protected: double _rate; unsigned _nb; @@ -58,6 +60,8 @@ class eoUniformBitMutation : public eoMonOp return _bitflip(chrom); } + virtual std::string className() const {return "eoUniformBitMutation";} + protected: double _rate; unsigned _nb; @@ -93,6 +97,8 @@ class eoConditionalBitMutation : public eoStandardBitMutation // thus one don't need to re-instantiate. return this->_bitflip(chrom); } + + virtual std::string className() const {return "eoConditionalBitMutation";} }; /** Shifted standard bit mutation with mutation rate p: @@ -125,6 +131,8 @@ class eoShiftedBitMutation : public eoStandardBitMutation // thus one don't need to re-instantiate. return this->_bitflip(chrom); } + + virtual std::string className() const {return "eoShiftedBitMutation";} }; /** Mutation which size is sample in a gaussian. @@ -163,6 +171,8 @@ class eoNormalBitMutation : public eoStandardBitMutation return this->_bitflip(chrom); } + virtual std::string className() const {return "eoNormalBitMutation";} + protected: double _variance; }; @@ -196,6 +206,8 @@ class eoFastBitMutation : public eoStandardBitMutation return this->_bitflip(chrom); } + virtual std::string className() const {return "eoFastBitMutation";} + protected: double powerlaw(unsigned n, double beta) diff --git a/eo/src/ga/make_genotype_ga.h b/eo/src/ga/make_genotype_ga.h index 93cab894d..3a1afaa84 100644 --- a/eo/src/ga/make_genotype_ga.h +++ b/eo/src/ga/make_genotype_ga.h @@ -68,7 +68,7 @@ eoInit & do_make_genotype(eoParser& _parser, eoState& _state, EOT, float _b // Then we can built a bitstring random initializer // based on boolean_generator class (see utils/rnd_generator.h) - eoBooleanGenerator * gen = new eoBooleanGenerator(_bias); + eoBooleanGenerator * gen = new eoBooleanGenerator(_bias); _state.storeFunctor(gen); eoInitFixedLength* init = new eoInitFixedLength(theSize, *gen); // store in state diff --git a/eo/src/utils/checkpointing b/eo/src/utils/checkpointing index ad190602a..60f2b81d4 100644 --- a/eo/src/utils/checkpointing +++ b/eo/src/utils/checkpointing @@ -50,7 +50,7 @@ #include "eoGenCounter.h" // and make_help - any better suggestion to include it? -void make_help(eoParser & _parser, bool exit_after = true); +void make_help(eoParser & _parser, bool exit_after = true, std::ostream& out = std::cout); #endif // !_CHECKPOINTING_ diff --git a/eo/src/utils/eoRndGenerators.h b/eo/src/utils/eoRndGenerators.h index 14cb8f822..e1168dcd2 100644 --- a/eo/src/utils/eoRndGenerators.h +++ b/eo/src/utils/eoRndGenerators.h @@ -115,15 +115,16 @@ inline bool eoUniformGenerator::operator()(void) to easily generate random booleans with a specified bias \ingroup bitstring */ -class eoBooleanGenerator : public eoRndGenerator +template +class eoBooleanGenerator : public eoRndGenerator { - public : - eoBooleanGenerator(float _bias = 0.5, eoRng& _rng = rng) : bias(_bias), gen(_rng) {} + public : + eoBooleanGenerator(float _bias = 0.5, eoRng& _rng = rng) : bias(_bias), gen(_rng) {} - bool operator()(void) { return gen.flip(bias); } - private : - float bias; - eoRng& gen; + T operator()(void) { return gen.flip(bias); } + private : + float bias; + eoRng& gen; }; /** diff --git a/eo/src/utils/make_help.cpp b/eo/src/utils/make_help.cpp index b7d5ff766..b021e0177 100644 --- a/eo/src/utils/make_help.cpp +++ b/eo/src/utils/make_help.cpp @@ -44,7 +44,7 @@ * It is declared in all make_xxx.h files in representation-dependent dirs * but it is NOT representation-dependent itself - that's why it's in utils */ -void make_help(eoParser & _parser, bool exit_after) +void make_help(eoParser & _parser, bool exit_after, std::ostream& out) { // name of the "status" file where all actual parameter values will be saved std::string str_status = _parser.ProgramName() + ".status"; // default value @@ -62,8 +62,8 @@ void make_help(eoParser & _parser, bool exit_after) // i.e. in case you need parameters somewhere else, postpone these if (_parser.userNeedsHelp()) { - _parser.printHelp(std::cout); - std::cout << "You can use an edited copy of file " << statusParam.value() + _parser.printHelp(out); + out << "You can use an edited copy of file " << statusParam.value() << " as parameter file" << std::endl; if(exit_after) { exit(0); diff --git a/eo/test/CMakeLists.txt b/eo/test/CMakeLists.txt index 5229f99f4..b6b9afee6 100644 --- a/eo/test/CMakeLists.txt +++ b/eo/test/CMakeLists.txt @@ -76,7 +76,10 @@ set (TEST_LIST t-algo-forged t-algo-forged-search t-FastGA + t-eoFastGA + t-forge-FastGA t-eoFoundryFastGA + t-eoAlgoFoundryFastGA ) diff --git a/eo/test/t-FastGA.cpp b/eo/test/t-FastGA.cpp new file mode 100644 index 000000000..548733673 --- /dev/null +++ b/eo/test/t-FastGA.cpp @@ -0,0 +1,79 @@ + +/* + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include + +#include +#include +#include "../../problems/eval/oneMaxEval.h" + +using EOT = eoBit; + +int main(int /*argc*/, char** /*argv*/) +{ + size_t dim = 5; + size_t pop_size = 3; + + oneMaxEval evalfunc; + eoPopLoopEval eval(evalfunc); + + eoBooleanGenerator gen(0.5); + eoInitFixedLength init(dim, gen); + + double cross_rate = 0.5; + eoProportionalOp cross; + // Cross-over that produce only one offspring, + // made by wrapping the quad op (which produce 2 offsprings) + // in a bin op (which ignore the second offspring). + eo1PtBitXover crossover; + eoQuad2BinOp mono_cross(crossover); + cross.add(mono_cross, cross_rate); + eoBinCloneOp bin_clone; + cross.add(bin_clone, 1 - cross_rate); // Clone + + double mut_rate = 0.5; + eoProportionalOp mut; + eoShiftedBitMutation mutation(0.5); + mut.add(mutation, mut_rate); + eoMonCloneOp mon_clone; + mut.add(mon_clone, 1 - mut_rate); // FIXME TBC + + eoSequentialOp variator; + variator.add(cross,1.0); + variator.add(mut,1.0); + + double lambda = 1.0; // i.e. 100% + eoStochTournamentSelect selector(0.5); + eoGeneralBreeder breeder(selector, variator, lambda); + + eoGenContinue common_cont(3); + eoCombinedContinue gen_cont(common_cont); + //gen_cont.add(continuator); + + eoPlusReplacement replacement; + + eoEasyEA algo = eoEasyEA(gen_cont, eval, breeder, replacement); + + eoPop pop; + pop.append(pop_size, init); + eval(pop,pop); + + algo(pop); + + std::cout << pop.best_element() << std::endl; +} diff --git a/eo/test/t-eoAlgoFoundryFastGA.cpp b/eo/test/t-eoAlgoFoundryFastGA.cpp new file mode 100644 index 000000000..b80d19eb5 --- /dev/null +++ b/eo/test/t-eoAlgoFoundryFastGA.cpp @@ -0,0 +1,147 @@ +#include +#include + +#include +#include +#include "../../problems/eval/oneMaxEval.h" + + +int main(int /*argc*/, char** /*argv*/) +{ + size_t dim = 500; + size_t pop_size = 10; + + eo::log << eo::setlevel(eo::warnings); + + using EOT = eoBit; + + oneMaxEval eval; + + eoBooleanGenerator gen(0.5); + eoInitFixedLength init(dim, gen); + + eoAlgoFoundryFastGA foundry(init, eval, pop_size*10); + + /***** Variation rates *****/ + for(double r = 0.0; r < 1.0; r+=0.1) { + foundry.crossover_rates.add(r); + foundry. mutation_rates.add(r); + } + + /***** Crossovers ****/ + foundry.crossovers.add< eo1PtBitXover >(); + foundry.crossovers.add< eoUBitXover >(0.5); // preference over 1 + for(size_t i=1; i < 11; i+=4) { + foundry.crossovers.add< eoNPtsBitXover >(i); // nb of points + } + + /***** Mutations ****/ + foundry.mutations.add< eoBitMutation >(0.01); // proba of flipping one bit + for(size_t i=1; i < 11; i+=4) { + foundry.mutations.add< eoDetBitFlip >(i); // mutate k bits + } + + /***** Selectors *****/ + for(eoOperatorFoundry>& ops : + {std::ref(foundry.crossover_selectors), + std::ref(foundry.aftercross_selectors), + std::ref(foundry.mutation_selectors) }) { + + ops.add< eoRandomSelect >(); + ops.add< eoStochTournamentSelect >(0.5); + ops.add< eoSequentialSelect >(); + ops.add< eoProportionalSelect >(); + for(size_t i=2; i < 10; i+=4) { + ops.add< eoDetTournamentSelect >(i); + } + } + + /***** Replacements ****/ + foundry.replacements.add< eoCommaReplacement >(); + foundry.replacements.add< eoPlusReplacement >(); + foundry.replacements.add< eoSSGAWorseReplacement >(); + foundry.replacements.add< eoSSGAStochTournamentReplacement >(0.51); + for(size_t i=2; i < 10; i+=4) { + foundry.replacements.add< eoSSGADetTournamentReplacement >(i); + } + + /***** Continuators ****/ + for(size_t i=10; i < 30; i+=10 ) { + foundry.continuators.add< eoSteadyFitContinue >(10,i); + } + + /***** Offspring population size *****/ + foundry.offspring_sizes.add(0); // 0 = same as parent pop + // for(size_t s = pop_size; s < 2*pop_size; s+=pop_size/10) { + // foundry.offspring_sizes.add(s); + // } + + + size_t n = + foundry.crossover_rates.size() + * foundry.crossover_selectors.size() + * foundry.crossovers.size() + * foundry.aftercross_selectors.size() + * foundry.mutation_rates.size() + * foundry.mutation_selectors.size() + * foundry.mutations.size() + * foundry.replacements.size() + * foundry.continuators.size() + * foundry.offspring_sizes.size(); + std::clog << n << " possible algorithms instances." << std::endl; + + EOT best_sol; + std::string best_algo = ""; + + size_t i=0; + for(size_t i_crossrate = 0; i_crossrate < foundry.crossover_rates.size(); ++i_crossrate ) { + for(size_t i_crossselect = 0; i_crossselect < foundry.crossover_selectors.size(); ++i_crossselect ) { + for(size_t i_cross = 0; i_cross < foundry.crossovers.size(); ++i_cross ) { + for(size_t i_aftercrosel = 0; i_aftercrosel < foundry.aftercross_selectors.size(); ++i_aftercrosel ) { + for(size_t i_mutrate = 0; i_mutrate < foundry.mutation_rates.size(); ++i_mutrate ) { + for(size_t i_mutselect = 0; i_mutselect < foundry.mutation_selectors.size(); ++i_mutselect ) { + for(size_t i_mut = 0; i_mut < foundry.mutations.size(); ++i_mut ) { + for(size_t i_rep = 0; i_rep < foundry.replacements.size(); ++i_rep ) { + for(size_t i_cont = 0; i_cont < foundry.continuators.size(); ++i_cont ) { + for(size_t i_pop = 0; i_pop < foundry.offspring_sizes.size(); ++i_pop ) { + std::clog << "\r" << i++ << "/" << n-1; std::clog.flush(); + + eoPop pop; + pop.append(pop_size, init); + + foundry.select({ + i_crossrate, + i_crossselect, + i_cross, + i_aftercrosel, + i_mutrate, + i_mutselect, + i_mut, + i_rep, + i_cont, + i_pop + }); + + // Actually perform a search + foundry(pop); + + if(best_sol.invalid()) { + best_sol = pop.best_element(); + best_algo = foundry.name(); + } else if(pop.best_element().fitness() > best_sol.fitness()) { + best_sol = pop.best_element(); + best_algo = foundry.name(); + } + } + } + } + } + } + } + } + } + } + } + std::cout << std::endl << "Best algo: " << best_algo << ", with " << best_sol << std::endl; + +} diff --git a/eo/test/t-eoFastGA.cpp b/eo/test/t-eoFastGA.cpp new file mode 100644 index 000000000..965a63b23 --- /dev/null +++ b/eo/test/t-eoFastGA.cpp @@ -0,0 +1,72 @@ + +/* + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include + +#include +#include +#include "../../problems/eval/oneMaxEval.h" + +using EOT = eoBit; + +int main(int /*argc*/, char** /*argv*/) +{ + size_t dim = 100; + size_t pop_size = 10; + + oneMaxEval evalfunc; + eoPopLoopEval eval(evalfunc); + + eoBooleanGenerator gen(0.5); + eoInitFixedLength init(dim, gen); + + double cross_rate = 0.5; + eoSequentialSelect select_cross; + eoUBitXover crossover; + eoRandomSelect select_aftercross; + + double mut_rate = 0.5; + eoSequentialSelect select_mut; + eoStandardBitMutation mutation(0.5); + + eoPlusReplacement replacement; + + eoGenContinue common_cont(dim*2); + eoCombinedContinue gen_cont(common_cont); + + eoFastGA algo( + cross_rate, + select_cross, + crossover, + select_aftercross, + mut_rate, + select_mut, + mutation, + eval, + replacement, + common_cont + ); + + eoPop pop; + pop.append(pop_size, init); + eval(pop,pop); + + algo(pop); + + std::cout << pop.best_element() << std::endl; +} diff --git a/eo/test/t-eoFoundryFastGA.cpp b/eo/test/t-eoFoundryFastGA.cpp index 2718f514e..45abe7d84 100644 --- a/eo/test/t-eoFoundryFastGA.cpp +++ b/eo/test/t-eoFoundryFastGA.cpp @@ -26,7 +26,7 @@ eoAlgoFoundryFastGA& make_foundry(eoFunctorStore& store, eoInit& ini } for(size_t i=5; i<100; i+=10) { - foundry.pop_sizes.add(i); + foundry.offspring_sizes.add(i); } /***** Crossovers ****/ @@ -51,14 +51,24 @@ eoAlgoFoundryFastGA& make_foundry(eoFunctorStore& store, eoInit& ini } /***** Selectors *****/ - foundry.selectors.add< eoRandomSelect >(); - foundry.selectors.add< eoSequentialSelect >(); - foundry.selectors.add< eoProportionalSelect >(); - for(size_t i=2; i < 10; i+=1) { // Tournament size. - foundry.selectors.add< eoDetTournamentSelect >(i); + for(eoOperatorFoundry>& ops : + {std::ref(foundry.crossover_selectors), + std::ref(foundry.aftercross_selectors), + std::ref(foundry.mutation_selectors) }) { + + ops.add< eoRandomSelect >(); + ops.add< eoStochTournamentSelect >(0.5); + ops.add< eoSequentialSelect >(); + ops.add< eoProportionalSelect >(); + for(size_t i=2; i < 10; i+=4) { + ops.add< eoDetTournamentSelect >(i); + } } - for(double i=0.51; i<0.91; i+=0.1) { // Tournament size as perc of pop. - foundry.selectors.add< eoStochTournamentSelect >(i); + + /***** Variation rates *****/ + for(double r = 0.0; r < 1.0; r+=0.1) { + foundry.crossover_rates.add(r); + foundry. mutation_rates.add(r); } /***** Replacements ****/ @@ -88,8 +98,20 @@ int main(int /*argc*/, char** /*argv*/) auto& foundry = make_foundry(store, init, onemax_eval); - size_t n = foundry.continuators.size() * foundry.crossovers.size() * foundry.mutations.size() * foundry.selectors.size() * foundry.replacements.size()* foundry.crossover_rates.size() * foundry.mutation_rates.size() * foundry.pop_sizes.size(); - std::clog << n << " possible algorithms instances." << std::endl; + + size_t n = + foundry.crossover_rates.size() + * foundry.crossover_selectors.size() + * foundry.crossovers.size() + * foundry.aftercross_selectors.size() + * foundry.mutation_rates.size() + * foundry.mutation_selectors.size() + * foundry.mutations.size() + * foundry.replacements.size() + * foundry.continuators.size() + * foundry.offspring_sizes.size(); + + std::clog << n << " possible algorithms instances." << std::endl; eoPop pop; pop.append(5,init); diff --git a/eo/test/t-forge-FastGA.cpp b/eo/test/t-forge-FastGA.cpp new file mode 100644 index 000000000..e4f713cff --- /dev/null +++ b/eo/test/t-forge-FastGA.cpp @@ -0,0 +1,115 @@ +#include +#include + +#include +#include +#include "../../problems/eval/oneMaxEval.h" + +int main(int /*argc*/, char** /*argv*/) +{ + size_t dim = 500; + size_t pop_size = 10; + + eo::log << eo::setlevel(eo::warnings); + + using EOT = eoBit; + + oneMaxEval eval; + + eoBooleanGenerator gen(0.5); + eoInitFixedLength init(dim, gen); + + eoGenContinue common_cont(100); + + eoForgeVector< eoContinue > continuators; + continuators.add< eoSteadyFitContinue >(10,10); + continuators.add< eoGenContinue >(100); + + eoForgeVector< eoQuadOp > crossovers; + crossovers.add< eo1PtBitXover >(); + crossovers.add< eoUBitXover >(0.5); // preference over 1 + crossovers.add< eoNPtsBitXover >(2); // nb of points + + eoForgeVector< eoMonOp > mutations; + mutations.add< eoBitMutation >(0.01); // proba of flipping one bit + mutations.add< eoDetBitFlip >(1); // mutate k bits + + eoForgeVector< eoSelectOne > selectors; + selectors.add< eoDetTournamentSelect >(pop_size/2); + selectors.add< eoStochTournamentSelect >(0.5); + selectors.add< eoSequentialSelect >(); + selectors.add< eoProportionalSelect >(); + + eoForgeVector< eoReplacement > replacors; + replacors.add< eoCommaReplacement >(); + replacors.add< eoPlusReplacement >(); + replacors.add< eoSSGAWorseReplacement >(); + replacors.add< eoSSGADetTournamentReplacement >(pop_size/2); + replacors.add< eoSSGAStochTournamentReplacement >(0.51); + + std::clog << continuators.size() * crossovers.size() * mutations.size() * selectors.size() * replacors.size() + << " possible algorithms instances." << std::endl; + + EOT best_sol; + std::string best_algo = ""; + + for(auto& forge_cont : continuators) { + auto& continuator = forge_cont->instantiate(); + + for(auto& forge_cross : crossovers) { + auto& crossover = forge_cross->instantiate(); + + for(auto& forge_mut : mutations ) { + auto& mutation = forge_mut->instantiate(); + + for(auto& forge_sel : selectors) { + auto& selector = forge_sel->instantiate(); + + for(auto& forge_rep : replacors) { + auto& replacor = forge_rep->instantiate(); + + std::ostringstream algo_name; + algo_name << continuator.className() << " + " + << crossover.className() << " + " + << mutation.className() << " + " + << selector.className() << " + " + << replacor.className(); + + std::clog << "ALGO: " << algo_name.str(); + std::clog.flush(); + + eoSequentialOp variator; + variator.add(crossover, 1.0); + variator.add(mutation, 1.0); + + eoGeneralBreeder breeder(selector, variator, 1.0); + + eoCombinedContinue gen_cont(common_cont); + gen_cont.add(continuator); + + eoEasyEA algo(gen_cont, eval, breeder, replacor); + + eoPop pop; + pop.append(pop_size, init); + ::apply(eval,pop); + + algo(pop); + + std::clog << " = " << pop.best_element().fitness() << std::endl; + + if(best_sol.invalid()) { + best_sol = pop.best_element(); + best_algo = algo_name.str(); + } else if(pop.best_element().fitness() > best_sol.fitness()) { + best_sol = pop.best_element(); + best_algo = algo_name.str(); + } + + } + } + } + } + } + std::cout << "Best algo: " << best_algo << ", with " << best_sol << std::endl; + +} diff --git a/problems/eval/eoEvalIOH.h b/problems/eval/eoEvalIOH.h index 7a5c1b5a6..9da86bdfc 100644 --- a/problems/eval/eoEvalIOH.h +++ b/problems/eval/eoEvalIOH.h @@ -200,7 +200,7 @@ class eoEvalIOHsuiteSingleDim : public eoEvalFunc // Evaluate the performance of the encoded algo instance // on a whole IOH suite benchmark. typename IOHprofiler_suite::Problem_ptr pb; - while(pb = _ioh_suite->get_next_problem()) { + while( (pb = _ioh_suite->get_next_problem()) ) { // Consider a new problem. _eval.problem(*pb); // Will call logger's target_problem.