diff --git a/cli/source/operon_gp.cpp b/cli/source/operon_gp.cpp index 6b74fda2..df375eda 100644 --- a/cli/source/operon_gp.cpp +++ b/cli/source/operon_gp.cpp @@ -384,7 +384,7 @@ auto main(int argc, char** argv) -> int }; gp.Run(executor, random, report); - fmt::print("{}\n", Operon::InfixFormatter::Format(best.Genotype, problem.GetDataset(), 6)); + fmt::print("{}\n", Operon::InfixFormatter::Format(best.Genotype, problem.GetDataset(), 8)); } catch (std::exception& e) { fmt::print(stderr, "error: {}\n", e.what()); return EXIT_FAILURE; diff --git a/cli/source/operon_nsgp.cpp b/cli/source/operon_nsgp.cpp index 867dd240..04a66174 100644 --- a/cli/source/operon_nsgp.cpp +++ b/cli/source/operon_nsgp.cpp @@ -36,6 +36,9 @@ #include "util.hpp" #include "operator_factory.hpp" +void Scale(Operon::Individual& ind, Operon::Span estimated, Operon::Span target, Operon::Scalar& a, Operon::Scalar& b); + + auto main(int argc, char** argv) -> int { auto opts = Operon::InitOptions("operon_gp", "Genetic programming symbolic regression"); @@ -300,23 +303,8 @@ auto main(int argc, char** argv) -> int Operon::Scalar a{1.0}; Operon::Scalar b{0.0}; auto linearScaling = taskflow.emplace([&]() { - auto [a_, b_] = Operon::FitLeastSquares(estimatedTrain, targetTrain); - a = static_cast(a_); - b = static_cast(b_); - // add scaling terms to the tree - auto& nodes = best.Genotype.Nodes(); - auto const sz = nodes.size(); - if (std::abs(a - Operon::Scalar{1}) > std::numeric_limits::epsilon()) { - nodes.emplace_back(Operon::Node::Constant(a)); - nodes.emplace_back(Operon::NodeType::Mul); - } - if (std::abs(b) > std::numeric_limits::epsilon()) { - nodes.emplace_back(Operon::Node::Constant(b)); - nodes.emplace_back(Operon::NodeType::Add); - } - if (nodes.size() > sz) { - best.Genotype.UpdateNodes(); - } + if (scale) + Scale(best, estimatedTrain, targetTrain, a, b); }); double r2Train{}; @@ -327,13 +315,17 @@ auto main(int argc, char** argv) -> int double maeTest{}; auto scaleTrain = taskflow.emplace([&]() { - Eigen::Map> estimated(estimatedTrain.data(), std::ssize(estimatedTrain)); - estimated = estimated * a + b; + if (scale) { + Eigen::Map> estimated(estimatedTrain.data(), std::ssize(estimatedTrain)); + estimated = estimated * a + b; + } }); auto scaleTest = taskflow.emplace([&]() { - Eigen::Map> estimated(estimatedTest.data(), std::ssize(estimatedTest)); - estimated = estimated * a + b; + if (scale) { + Eigen::Map> estimated(estimatedTest.data(), std::ssize(estimatedTest)); + estimated = estimated * a + b; + } }); auto calcStats = taskflow.emplace([&]() { @@ -358,9 +350,13 @@ auto main(int argc, char** argv) -> int auto calculateOffMemory = taskflow.transform_reduce(off.begin(), off.end(), totalMemory, std::plus{}, [&](auto const& ind) { return getSize(ind); }); // define task graph - linearScaling.succeed(evalTrain, evalTest); - linearScaling.precede(scaleTrain, scaleTest); - calcStats.succeed(scaleTrain, scaleTest); + //if (scale) { + linearScaling.succeed(evalTrain, evalTest); + linearScaling.precede(scaleTrain, scaleTest); + calcStats.succeed(scaleTrain, scaleTest); + //} else { + // calcStats.succeed(evalTrain, evalTest); + //} calcStats.precede(calculateLength, calculateQuality, calculatePopMemory, calculateOffMemory); executor.corun(taskflow); @@ -394,7 +390,27 @@ auto main(int argc, char** argv) -> int }; gp.Run(executor, random, report); - fmt::print("{}\n", Operon::InfixFormatter::Format(best.Genotype, problem.GetDataset(), std::numeric_limits::digits)); + fmt::print("Best individual:\n"); + fmt::print("{}\n", Operon::InfixFormatter::Format(best.Genotype, problem.GetDataset(), 8)); + + auto const& pop = gp.Parents(); + + // print all solutions in the first front + if (result["show-pareto-front"].as()) { + fmt::print("All individuals in the Pareto front:\n"); + for(auto ind = pop.begin(); ind < pop.end(); ind++) { + Operon::Individual cur = *ind; + if(cur.Rank == 0) { + if (scale) { + Operon::Scalar a{1.0}; + Operon::Scalar b{0.0}; + auto estimatedTrain = Operon::Interpreter::Evaluate(cur.Genotype, problem.GetDataset(), trainingRange); + Scale(cur, estimatedTrain, targetTrain, a, b); + } + fmt::print("{}\n", Operon::InfixFormatter::Format(cur.Genotype, problem.GetDataset(), 8)); + } + } + } } catch (std::exception& e) { fmt::print(stderr, "error: {}\n", e.what()); return EXIT_FAILURE; @@ -402,3 +418,25 @@ auto main(int argc, char** argv) -> int return 0; } + + + +void Scale(Operon::Individual& ind, Operon::Span estimated, Operon::Span target, Operon::Scalar& a, Operon::Scalar& b) { + auto [a_, b_] = Operon::FitLeastSquares(estimated, target); + a = static_cast(a_); + b = static_cast(b_); + // add scaling terms to the tree + auto& nodes = ind.Genotype.Nodes(); + auto const sz = nodes.size(); + if (std::abs(a - Operon::Scalar{1}) > std::numeric_limits::epsilon()) { + nodes.emplace_back(Operon::Node::Constant(a)); + nodes.emplace_back(Operon::NodeType::Mul); + } + if (std::abs(b) > std::numeric_limits::epsilon()) { + nodes.emplace_back(Operon::Node::Constant(b)); + nodes.emplace_back(Operon::NodeType::Add); + } + if (nodes.size() > sz) { + ind.Genotype.UpdateNodes(); + } +} diff --git a/cli/source/util.cpp b/cli/source/util.cpp index 30581506..0f4ad0d9 100644 --- a/cli/source/util.cpp +++ b/cli/source/util.cpp @@ -183,6 +183,7 @@ auto InitOptions(std::string const& name, std::string const& desc, int width) -> ("disable-symbols", "Comma-separated list of disabled symbols ("+symbols+")", cxxopts::value()) ("symbolic", "Operate in symbolic mode - no coefficient tuning or coefficient mutation", cxxopts::value()->default_value("false")) ("show-primitives", "Display the primitive set used by the algorithm") + ("show-pareto-front", "Displays all expressions in the first Pareto front of the final generation", cxxopts::value()->default_value("false")) ("threads", "Number of threads to use for parallelism", cxxopts::value()->default_value("0")) ("timelimit", "Time limit after which the algorithm will terminate", cxxopts::value()->default_value(std::to_string(std::numeric_limits::max()))) ("debug", "Debug mode (more information displayed)") diff --git a/source/formatter/infix.cpp b/source/formatter/infix.cpp index 2fc85e1b..a7a53170 100644 --- a/source/formatter/infix.cpp +++ b/source/formatter/infix.cpp @@ -10,10 +10,10 @@ auto InfixFormatter::FormatNode(Tree const& tree, Operon::Mapsecond); } else { @@ -22,7 +22,7 @@ auto InfixFormatter::FormatNode(Tree const& tree, Operon::Map