symbolic_regression_part3 - morinim/vita GitHub Wiki

Symbolic regression - Custom evaluator and teams

A custom evaluator is nice but not enough. What I really need is a way to evolve multiple programs at the same time and then apply my evaluator. Can it be done?

Yes, it can. You can use a team.

(Not so) toy problem

$$\begin{pmatrix} a_1 \\ a_2 \\ \vdots \\ a_N \end{pmatrix} = \begin{pmatrix} b_{11} & b_{12} & \ldots & b_{1N} \\ b_{21} & b_{22} & \ldots & b_{2N} \\ \vdots & \vdots & \vdots & \vdots \\ b_{N1} & b_{N2} & \ldots & b_{NN} \end{pmatrix} \cdot \begin{pmatrix} f_1(c) \\ f_2(c) \\ \vdots \\ f_N(c) \end{pmatrix}$$

This is similar to the previous problem but requires the evolution of multiple candidate solutions at the same time.

Setting up code

const auto a = get_vector();  // N-dimensional vector
const auto b = get_matrix();  // NxN matrix

This time a and b aren't scalar. We use two support functions to generate a random vector and a random matrix of the appropriate dimensions.


Once again the evaluator needs various remarks:

using candidate_solution = vita::team<vita::i_mep>;

class my_evaluator : public vita::evaluator<candidate_solution>
{
public:
  vita::fitness_t operator()(const candidate_solution &x) override
  {
    std::vector<double> f(N);
    std::transform(x.begin(), x.end(), f.begin(),
                   [](const auto &prg)
                   {
                     const auto ret(vita::run(prg));

                     return vita::has_value(ret) ? std::get<vita::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 candidate_solution is a team of individuals/programs. The members of a team undergo a coevolutionary process because they are always selected, evaluated and varied simultaneously.

A line by line description of the evaluation process follows:

std::vector<double> f(N);
std::transform(x.begin(), x.end(), f.begin(),
               [](const auto &prg)
               {
                 const auto ret(vita::run(prg));

                 return vita::has_value(ret) ? std::get<vita::D_DOUBLE>(ret)
                                             : 0.0;
               });

This time f is a vector (where we store the results of the components of the team/candidate_solution).

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:

$$ model[i] = \sum_{j=0}^{N} b_{ij} \cdot f_j(c) $$

double delta(std::inner_product(a.begin(), a.end(), model.begin(), 0.0,
                                std::plus<>(),
                                [](auto v1, auto v2)
                                {
                                  return std::fabs(v1 - v2);
                                }));

As before delta is a measure of the error based on the absolute value (std::inner_product performs ordered map/reduce operation on a and model) . Mathematically:

$$ delta = \sum_{i=0}^{N} \left\lvert a_i - model[i] \right\rvert $$


Only one line of the main() function varies:

prob.env.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)

⚠️ **GitHub.com Fallback** ⚠️