12enum class MutationType {
23MutationType mutation_type_from_string(
const std::string& choice) {
24 if (choice ==
"point")
25 return MutationType::Point;
26 if (choice ==
"insert")
27 return MutationType::Insert;
28 if (choice ==
"delete")
29 return MutationType::Delete;
30 if (choice ==
"subtree")
31 return MutationType::Subtree;
32 if (choice ==
"toggle_weight_on")
33 return MutationType::ToggleWeightOn;
34 if (choice ==
"toggle_weight_off")
35 return MutationType::ToggleWeightOff;
37 return MutationType::Crossover;
38 return MutationType::Unknown;
41const char* mutation_type_to_string(MutationType choice) {
43 case MutationType::Point:
45 case MutationType::Insert:
47 case MutationType::Delete:
49 case MutationType::Subtree:
51 case MutationType::ToggleWeightOn:
52 return "toggle_weight_on";
53 case MutationType::ToggleWeightOff:
54 return "toggle_weight_off";
55 case MutationType::Crossover:
57 case MutationType::Unknown:
74 template<Brush::ProgramType T>
87 if (
IsWeighable(spot.node->data.node_type) && spot.node->data.weight_is_fixed){
91 (*newNode).W = spot.node->data.W;
92 (*newNode).set_is_weighted(
true);
93 (*newNode).weight_is_fixed=
true;
97 program.
Tree.replace(spot, *newNode);
112 template<Brush::ProgramType T>
116 vector<float> weights;
120 std::transform(program.
Tree.begin(), program.
Tree.end(), std::back_inserter(weights),
122 size_t d = 1+program.Tree.depth(iter);
123 std::advance(iter, 1);
126 if ((d >= params.get_max_depth())
127 || (variator.search_space.node_map.find(n.ret_type) == variator.search_space.node_map.end())) {
131 return n.get_prob_change();
137 weights.resize(program.Tree.size());
138 std::fill(weights.begin(), weights.end(), 0.0f);
144 template<Brush::ProgramType T>
148 auto spot_type = spot.node->data.ret_type;
159 spot_type, spot_type, params.
max_size-program.
Tree.size()-1);
166 if (
IsWeighable(spot.node->data.node_type) && spot.node->data.weight_is_fixed){
167 Node& prev_n = spot.node->data;
172 (*n).
W = spot.node->data.W;
173 (*n).set_is_weighted(
true);
174 (*n).weight_is_fixed=
true;
182 auto parent_node = program.
Tree.wrap(spot, *n);
185 bool spot_filled =
false;
186 for (
auto a: (*n).arg_types)
196 program.
Tree.append_child(parent_node, opt.value());
199 else if (a == spot_type)
208 program.
Tree.insert(spot, opt.value());
225 template<Brush::ProgramType T>
229 vector<float> weights(program.
Tree.size());
232 std::transform(program.
Tree.begin(), program.
Tree.end(), weights.begin(),
235 if (n.weight_is_fixed){
241 return n.get_prob_change();
248 template<Brush::ProgramType T>
260 program.
Tree.erase_children(spot);
262 program.
Tree.replace(spot, opt.value());
277 template<Brush::ProgramType T>
281 vector<float> weights(program.
Tree.size());
284 std::transform(program.
Tree.begin(), program.
Tree.end(), weights.begin(),
287 if (Is<NodeType::OffsetSum>(n.node_type) || Is<NodeType::Constant>(n.node_type))
291 if ((!n.get_is_weighted())
292 && (!n.weight_is_fixed)
293 && IsWeighable(n.node_type))
295 return n.get_prob_change();
303 std::fill(weights.begin(), weights.end(), 0.0f);
309 template<Brush::ProgramType T>
313 if (spot.node->data.get_is_weighted()==
true
317 spot.node->data.set_is_weighted(
true);
331 template<Brush::ProgramType T>
335 vector<float> weights(program.
Tree.size());
337 std::transform(program.
Tree.begin(), program.
Tree.end(), weights.begin(),
340 if (Is<NodeType::OffsetSum>(n.node_type) || Is<NodeType::Constant>(n.node_type))
343 if (n.get_is_weighted()
344 && (!n.weight_is_fixed)
345 && IsWeighable(n.node_type))
346 return n.get_prob_change();
354 template<Brush::ProgramType T>
358 if (spot.node->data.get_is_weighted()==
false)
361 spot.node->data.set_is_weighted(
false);
375 template<Brush::ProgramType T>
379 vector<float> weights;
386 std::transform(program.
Tree.begin(), program.
Tree.end(), std::back_inserter(weights),
388 size_t d = program.Tree.depth(iter);
389 size_t s = program.Tree.size(iter);
390 std::advance(iter, 1);
393 if ((d >= params.max_depth)
394 || (node_map.find(n.ret_type) == node_map.end()) )
397 return n.get_prob_change();
403 template<Brush::ProgramType T>
415 auto spot_type = spot.node->data.ret_type;
428 s =
r.rnd_int(1, s+1);
440 if (
IsWeighable(spot.node->data.node_type) && spot.node->data.weight_is_fixed){
441 Node& n = subtree.value().begin().node->data;
444 n.
W = spot.node->data.W;
450 program.
Tree.erase_children(spot);
452 program.
Tree.move_ontop(spot, subtree.value().begin());
481template<Brush::ProgramType T>
491 vector<float> child_weights(child.
Tree.size());
493 auto child_iter = child.
Tree.begin();
494 std::transform(child.
Tree.begin(), child.
Tree.end(), child_weights.begin(),
496 auto s_at = child.size_at(child_iter);
497 auto d_at = child.depth_to_reach(child_iter);
499 std::advance(child_iter, 1);
505 d_at<parameters.max_depth
507 return n.get_prob_change();
513 if (std::all_of(child_weights.begin(), child_weights.end(), [](
const auto& w) {
524 while (++attempts <= 3)
526 auto child_spot =
r.select_randomly(child.
Tree.begin(),
528 child_weights.begin(),
532 auto child_ret_type = child_spot.node->data.ret_type;
539 vector<float> other_weights(other.
Tree.size());
542 auto other_iter = other.
Tree.begin();
543 std::transform(other.
Tree.begin(), other.
Tree.end(), other_weights.begin(),
544 [&other, &other_iter, allowed_size, allowed_depth, child_ret_type](
const auto& n)
mutable {
545 int s = other.size_at(other_iter);
546 int d = other.depth_at(other_iter);
548 std::advance(other_iter, 1);
551 if ( (s <= allowed_size)
552 && (d <= allowed_depth)
553 && (n.ret_type == child_ret_type)
555 return n.get_prob_change();
562 bool matching_spots_found = std::any_of(other_weights.begin(), other_weights.end(),
563 [](
float w) { return w > 0.0f; });
565 if (matching_spots_found) {
566 auto other_spot =
r.select_randomly(
567 other.
Tree.begin(), other.
Tree.end(),
568 other_weights.begin(), other_weights.end()
572 if (
IsWeighable(child_spot.node->data.node_type) && child_spot.node->data.weight_is_fixed){
573 Node& n = other_spot.node->data;
576 n.
W = child_spot.node->data.W;
583 child.
Tree.move_ontop(child_spot, other_spot);
586 ind.
set_variation(mutation_type_to_string(MutationType::Crossover));
635template<Brush::ProgramType T>
643 bool all_zero =
true;
645 if (it.second > 0.0) {
656 choice =
r.random_choice(
parameters.mutation_probs);
659 const auto mutation_choice = mutation_type_from_string(choice);
660 if (mutation_choice == MutationType::Unknown) {
661 std::string msg = fmt::format(
"{} not a valid mutation choice", choice);
667 vector<float> weights;
668 switch (mutation_choice) {
669 case MutationType::Point:
672 case MutationType::Insert:
675 case MutationType::Delete:
678 case MutationType::Subtree:
681 case MutationType::ToggleWeightOn:
684 case MutationType::ToggleWeightOff:
687 case MutationType::Crossover:
688 case MutationType::Unknown:
693 if (std::all_of(weights.begin(), weights.end(), [](
const auto& w) {
701 while(attempts++ < 3)
706 auto spot =
r.select_randomly(child.
Tree.begin(), child.
Tree.end(),
707 weights.begin(), weights.end());
714 switch (mutation_choice) {
715 case MutationType::Point:
718 case MutationType::Insert:
721 case MutationType::Delete:
724 case MutationType::Subtree:
727 case MutationType::ToggleWeightOn:
730 case MutationType::ToggleWeightOff:
733 case MutationType::Crossover:
734 case MutationType::Unknown:
759 if (choice.compare(
"point") == 0
760 || choice.compare(
"insert") == 0
761 || choice.compare(
"delete") == 0
775template<Brush::ProgramType T>
777 const vector<size_t>& parents)
781 for (
unsigned i = 0; i<indices.size(); ++i)
789 std::optional<Individual<T>> opt=std::nullopt;
792 *
r.select_randomly(parents.begin(), parents.end())];
794 vector<Individual<T>> ind_parents;
800 *
r.select_randomly(parents.begin(), parents.end())];
802 auto variation_result =
cross(mom, dad);
803 ind_parents = {mom, dad};
804 opt = variation_result;
808 auto variation_result =
mutate(mom);
811 opt = variation_result;
857 pop.
individuals.at(indices.at(i)) = std::make_shared<Individual<T>>(ind);
861template <Brush::ProgramType T>
869 if (variation_probs.find(
"cx") != variation_probs.end())
870 parameters.set_cx_prob(variation_probs.at(
"cx"));
872 for (
const auto& variation : variation_probs)
873 if (variation.first !=
"cx")
874 parameters.mutation_probs[variation.first] = variation.second;
878 auto datatype = bandit.first;
880 auto terminal_probs = bandit.second.sample_probs(
true);
882 for (
auto& [terminal_name, terminal_prob] : terminal_probs) {
884 auto it = std::find_if(
887 [&](
auto& node) { return node.get_feature() == terminal_name; });
889 if (it ==
search_space.terminal_map.at(datatype).end()) {
893 auto index = std::distance(
search_space.terminal_map.at(datatype).begin(), it);
896 search_space.terminal_weights.at(datatype)[index] = terminal_prob;
901 for (
auto& [ret_type, bandit_map] :
op_bandits) {
902 for (
auto& [args_type, bandit] : bandit_map) {
903 auto op_probs = bandit.sample_probs(
true);
905 for (
auto& [op_name, op_prob] : op_probs) {
906 bool updated =
false;
907 for (
const auto& [node_type, node_value]:
search_space.node_map.at(ret_type).at(args_type))
909 if (node_value.name == op_name) {
911 search_space.node_map_weights.at(ret_type).at(args_type).at(node_type) = op_prob;
void set_variation(string v)
void set_sampled_nodes(const vector< Node > &nodes)
Fitness fitness
aggregate fitness score
vector< string > get_objectives() const
void set_objectives(vector< string > objs)
void set_parents(const vector< Individual< T > > &parents)
void init(SearchSpace &ss, const Parameters ¶ms)
Program< T > program
executable data structure
vector< size_t > get_island_indexes(int island)
vector< std::shared_ptr< Individual< T > > > individuals
delete subtree and replace it with a terminal of the same return type
static auto mutate(Program< T > &program, Iter spot, Variation< T > &variator, const Parameters ¶ms)
static auto find_spots(Program< T > &program, Variation< T > &variator, const Parameters ¶ms)
insert a node with spot as a child
static auto mutate(Program< T > &program, Iter spot, Variation< T > &variator, const Parameters ¶ms)
static auto find_spots(Program< T > &program, Variation< T > &variator, const Parameters ¶ms)
static auto find_spots(Program< T > &program, Variation< T > &variator, const Parameters ¶ms)
tree< Node >::pre_order_iterator Iter
replace node with same typed node
static auto mutate(Program< T > &program, Iter spot, Variation< T > &variator, const Parameters ¶ms)
replaces the subtree rooted in spot
static auto find_spots(Program< T > &program, Variation< T > &variator, const Parameters ¶ms)
static auto mutate(Program< T > &program, Iter spot, Variation< T > &variator, const Parameters ¶ms)
toggle the node's weight OFF
static auto find_spots(Program< T > &program, Variation< T > &variator, const Parameters ¶ms)
static auto mutate(Program< T > &program, Iter spot, Variation< T > &variator, const Parameters ¶ms)
toggle the node's weight ON
static auto mutate(Program< T > &program, Iter spot, Variation< T > &variator, const Parameters ¶ms)
static auto find_spots(Program< T > &program, Variation< T > &variator, const Parameters ¶ms)
Class representing the variation operators in Brush.
map< DataType, map< size_t, Bandit > > op_bandits
void vary(Population< T > &pop, int island, const vector< size_t > &parents)
Handles variation of a population.
std::optional< Node > bandit_sample_op_with_arg(DataType ret, DataType arg, int max_args=0)
std::optional< Individual< T > > cross(const Individual< T > &mom, const Individual< T > &dad)
Performs croearch_spaceover operation on two individuals.
std::optional< Node > bandit_sample_terminal(DataType R)
std::optional< Node > bandit_get_node_like(Node node)
map< DataType, Bandit > terminal_bandits
std::optional< Individual< T > > mutate(const Individual< T > &parent, string choice="")
Performs mutation operation on an individual.
#define HANDLE_ERROR_THROW(err)
< nsga2 selection operator for getting the front
auto IsWeighable() noexcept -> bool
void set_linear_complexity(unsigned int new_lc)
void set_complexity(unsigned int new_c)
void set_loss_v(float f_v)
void set_depth(unsigned int new_d)
unsigned int get_complexity() const
void set_size(unsigned int new_s)
unsigned int get_depth() const
unsigned int get_linear_complexity() const
unsigned int get_size() const
class holding the data for a node in a tree.
bool weight_is_fixed
whether the weight should be kept during variation. Notice that weight_is_fixed alows us to fix the w...
void set_is_weighted(bool is_weighted)
float W
the weights of the node. also used for splitting thresholds.
unsigned get_max_size() const
An individual program, a.k.a. model.
int size_at(Iter &top, bool include_weight=true) const
count the size of a given subtree, optionally including the weights in weighted nodes....
int depth() const
count the tree depth of the program. The depth is not influenced by weighted nodes.
int depth_to_reach(Iter &top) const
count the depth until reaching the given subtree. The depth is not influenced by weighted nodes....
int size(bool include_weight=true) const
count the tree size of the program, including the weights in weighted nodes.
Map< Node > node_map
Maps return types to argument types to node types.
std::optional< tree< Node > > sample_subtree(Node root, int max_d, int max_size) const
create a subtree with maximum size and depth restrictions and root of type root_type