-
-
Notifications
You must be signed in to change notification settings - Fork 3
symbolic_regression_part3
A custom evaluator is often not enough. In some problems, we need to evolve multiple programs simultaneously and evaluate them as a whole.
Can this be done?
Yes, it's possible using a team.
This extends the previous problem: instead of evolving a single function, we now evolve a set of functions that are evaluated jointly.
const auto a = get_vector(); // N-dimensional vector const auto b = get_matrix(); // NxN matrix
This time, a and b are no longer scalars. We use helper functions to generate a random vector and a matrix of compatible dimensions.
using candidate_solution = ultra::gp::team<ultra::gp::individual>;The candidate solution is now a team of individuals (i.e. multiple programs evolved together). Conceptually, each individual in the team learns a component of the solution, and the final output emerges from their combination.
Once again, the evaluator needs various changes:
// Given a team (i.e. a candidate solution of the problem), returns a score // measuring how good it is. [[nodiscard]] double my_evaluator(const candidate_solution &x) { using namespace ultra; std::vector<double> f(N); std::ranges::transform( x, f.begin(), [](const auto &prg) { const auto ret(run(prg)); return has_value(ret) ? std::get<D_DOUBLE>(ret) : 0.0; }); std::vector<double> model(N, 0.0); for (unsigned i(0); i < N; ++i) for (unsigned j(0); j < N; ++j) model[i] += b(i, j) * f[j]; double delta(std::inner_product(a.begin(), a.end(), model.begin(), 0.0, std::plus{}, [](auto v1, auto v2) { return std::fabs(v1 - v2); })); return -delta; }
The members of a team are always selected, evaluated, and varied together, effectively undergoing a co-evolutionary process.
A line-by-line description of the evaluation process follows:
std::vector<double> f(N); std::ranges::transform( x, f.begin(), [](const auto &prg) { const auto ret(run(prg)); return has_value(ret) ? std::get<D_DOUBLE>(ret) : 0.0; });
Here, f is a vector storing the outputs of each program in the team.
std::vector<double> model(N, 0.0); for (unsigned i(0); i < N; ++i) for (unsigned j(0); j < N; ++j) model[i] += b(i, j) * f[j];
Mathematically the code is equivalent to:
This corresponds to a standard matrix–vector multiplication.
As before, delta measures the error using the absolute difference:
double delta(std::inner_product(a.begin(), a.end(), model.begin(), 0.0, std::plus{}, [](auto v1, auto v2) { return std::fabs(v1 - v2); }));
std::inner_product performs an ordered map/reduce operation on a and model. Mathematically:
Only one line of the main() function varies:
prob.params.team.individuals = N;
to inform the search engine of the team size.
(for your ease, all the code is in the examples/symbolic_regression04.cc file)