Improving coherence of size and depth and error handling when sampling in search space
The size and depth of the program were calculated as the size and depth of the underlying tree structure holding the nodes, but this didn't take into account the weights and multiplication operators in weighted nodes.
Also, mutation and crossover may fail if the search space does not hold a non-empty solution space, and we needed to handle this more gracefully than just ignoring it.
This pull request improved the source code in order to solve these problems.
This branch was originally intended to implement a multi-armed bandit (MAB) schema to optimize mutation weights during the evolution, but we felt the need of having better estimates of the size and depth of the program, and we found some particular cases where the search space could fail (implying that mutation would fail as well). The modifications made to achieve a good code to start working on MAB involved several redesigns that should not be kept apart from the main branch.
All documentation in the PR is up to date with these changes, and it passes all cpp and python tests.
Major changes
Search space functions get_<something> that are based on randomly sampling nodes were renamed to sample_<something>;
The sample_<something> functions now returns std::optional that may have a sampled node (if the search space has at least one node that satisfies the conditions and has a strictly positive weight associated with it;
Variation methods based on sampling from search space now return std::optional that may contain a new expression;
PTC2 was updated to work with the
Users should be aware of this behavior, as the python wrapper was also updated to return either None or the mutated expression;
Python's NSGA-II algorithm was redesigned to work with the possibility that mutation and crossover may fail;
Program now has its own function to get the size (now we use PRG.size() instead of PRG.Tree.size()), and has an optional argument to include node weights into the calculation;
Program has also its own function to get depth (PRG.depth()) and get the depth at some node (PRG.depth_at);
Mutation and crossover are context-aware in the sense that they can check for the program depth and size after chosing an spot to see candidate operators to use;
Mutation and crossover will return std::nullopt if it creates an expression bigger than PARAMS["max_size"] or PARAMS["max_depth"].
Minor changes
New cpp tests;
Updated version of previous tests to work with new behaviors of variation operators;
got rid of some compiler warnings;
Brush's errors can now be captured by GTEST, making it easier to test throws;
Boolean nodes cannot be weighted;
Bug fixes
Fixed bug from issue #37. The bug existed beause the search space didn't create a boolean hash due to a missing parameter. To add this parameter, we needed to change how we access node's weights through get_weight.
Improving coherence of size and depth and error handling when sampling in search space
The size and depth of the program were calculated as the size and depth of the underlying tree structure holding the nodes, but this didn't take into account the weights and multiplication operators in weighted nodes.
Also, mutation and crossover may fail if the search space does not hold a non-empty solution space, and we needed to handle this more gracefully than just ignoring it.
This pull request improved the source code in order to solve these problems.
All documentation in the PR is up to date with these changes, and it passes all cpp and python tests.
Major changes
get_<something>
that are based on randomly sampling nodes were renamed tosample_<something>
;sample_<something>
functions now returnsstd::optional
that may have a sampled node (if the search space has at least one node that satisfies the conditions and has a strictly positive weight associated with it;std::optional
that may contain a new expression;None
or the mutated expression;PRG.size()
instead ofPRG.Tree.size()
), and has an optional argument to include node weights into the calculation;PRG.depth()
) and get the depth at some node (PRG.depth_at
);std::nullopt
if it creates an expression bigger thanPARAMS["max_size"]
orPARAMS["max_depth"]
.Minor changes
Bug fixes
get_weight
.