Brush C++ API
A flexible interpretable machine learning framework
Loading...
Searching...
No Matches
variation.cpp
Go to the documentation of this file.
1#include "variation.h"
2
3namespace Brush {
4namespace Var {
5
6
7using namespace Brush;
8using namespace Pop;
9using namespace MAB;
10
18{
19public:
20 template<Brush::ProgramType T>
21 static auto mutate(Program<T>& program, Iter spot, Variation<T>& variator,
22 const Parameters& params)
23 {
24 // get_node_like will sample a similar node based on node_map_weights or
25 // terminal_weights, and maybe will return a Node.
26
27 // cout << "point mutation" << std::endl;
28 auto context = variator.get_context(program, spot);
29 optional<Node> newNode = variator.bandit_get_node_like(spot.node->data, context);
30
31 if (!newNode) // overload to check if newNode == nullopt
32 return false;
33
34 // if optional contains a Node, we access its contained value
35 program.Tree.replace(spot, *newNode);
36
37 return true;
38 }
39};
40
48{
49public:
50 template<Brush::ProgramType T>
51 static auto find_spots(Program<T>& program, Variation<T>& variator,
52 const Parameters& params)
53 {
54 vector<float> weights;
55
56 if (program.Tree.size() < params.get_max_size()) {
57 Iter iter = program.Tree.begin();
58 std::transform(program.Tree.begin(), program.Tree.end(), std::back_inserter(weights),
59 [&](const auto& n){
60 size_t d = 1+program.Tree.depth(iter);
61 std::advance(iter, 1);
62
63 // check if SS holds an operator to avoid failing `check` in sample_op_with_arg
64 if ((d >= params.get_max_depth())
65 || (variator.search_space.node_map.find(n.ret_type) == variator.search_space.node_map.end())) {
66 return 0.0f;
67 }
68 else {
69 return n.get_prob_change();
70 }
71 });
72 }
73 else {
74 // fill the vector with zeros, since we're already at max_size
75 weights.resize(program.Tree.size());
76 std::fill(weights.begin(), weights.end(), 0.0f);
77 }
78
79 return weights;
80 }
81
82 template<Brush::ProgramType T>
83 static auto mutate(Program<T>& program, Iter spot, Variation<T>& variator,
84 const Parameters& params)
85 {
86 auto spot_type = spot.node->data.ret_type;
87
88 // pick a random compatible node to insert (with probabilities given by
89 // node_map_weights). The `-1` represents the node being inserted.
90 // Ideally, it should always find at least one match (the same node
91 // used as a reference when calling the function). However, we have a
92 // size restriction, which will be relaxed here (just as it is in the PTC2
93 // algorithm). This mutation can create a new expression that exceeds the
94 // maximum size by the highest arity among the operators.
95 // cout << "insert mutation" << std::endl;
96 auto context = variator.get_context(program, spot);
97 std::optional<Node> n = variator.bandit_sample_op_with_arg(
98 spot_type, spot_type, context, params.max_size-program.Tree.size()-1);
99
100 if (!n) // there is no operator with compatible arguments
101 return false;
102
103 // make node n wrap the subtree at the chosen spot
104 auto parent_node = program.Tree.wrap(spot, *n);
105
106 // now fill the arguments of n appropriately
107 bool spot_filled = false;
108 for (auto a: (*n).arg_types)
109 {
110 if (spot_filled)
111 {
112 // if spot is in its child position, append children.
113 // auto context = variator.get_context<T>(program.Tree, spot);
114 auto opt = variator.bandit_sample_terminal(a, context);
115
116 if (!opt)
117 return false;
118
119 program.Tree.append_child(parent_node, opt.value());
120 }
121 // if types match, treat this spot as filled by the spot node
122 else if (a == spot_type)
123 spot_filled = true;
124 // otherwise, add siblings before spot node
125 else {
126 // auto context = variator.get_context<T>(program.Tree, spot);
127 auto opt = variator.bandit_sample_terminal(a, context);
128
129 if (!opt)
130 return false;
131
132 program.Tree.insert(spot, opt.value());
133 }
134 }
135
136 return true;
137 }
138};
139
147{
148public:
149 template<Brush::ProgramType T>
150 static auto mutate(Program<T>& program, Iter spot, Variation<T>& variator,
151 const Parameters& params)
152 {
153 // sample_terminal will sample based on terminal_weights. If it succeeds,
154 // then the new terminal will be in `opt.value()`
155
156 // cout << "delete mutation" << std::endl;
157 auto context = variator.get_context(program, spot);
158 auto opt = variator.bandit_sample_terminal(spot.node->data.ret_type, context);
159
160 if (!opt) // there is no terminal with compatible arguments
161 return false;
162
163 program.Tree.erase_children(spot);
164
165 program.Tree.replace(spot, opt.value());
166
167 return true;
168 }
169};
170
178{
179public:
180 template<Brush::ProgramType T>
181 static auto find_spots(Program<T>& program, Variation<T>& variator,
182 const Parameters& params)
183 {
184 vector<float> weights(program.Tree.size());
185
186 if (program.Tree.size() < params.max_size) {
187 std::transform(program.Tree.begin(), program.Tree.end(), weights.begin(),
188 [&](const auto& n){
189 // some nodetypes must always have a weight
190 if (Is<NodeType::OffsetSum>(n.node_type))
191 return 0.0f;
192
193 // only weighted nodes can be toggled off
194 if (!n.get_is_weighted()
195 && IsWeighable(n.node_type))
196 {
197 return n.get_prob_change();
198 }
199 else
200 return 0.0f;
201 });
202 }
203 else {
204 // fill the vector with zeros, since we're already at max_size
205 std::fill(weights.begin(), weights.end(), 0.0f);
206 }
207
208 return weights;
209 }
210
211 template<Brush::ProgramType T>
212 static auto mutate(Program<T>& program, Iter spot, Variation<T>& variator,
213 const Parameters& params)
214 {
215 if (spot.node->data.get_is_weighted()==true // cant turn on whats already on
216 || !IsWeighable(spot.node->data.node_type)) // does not accept weights (e.g. boolean)
217 return false; // false indicates that mutation failed and should return std::nullopt
218
219 spot.node->data.set_is_weighted(true);
220 return true;
221 }
222};
223
231{
232public:
233 template<Brush::ProgramType T>
234 static auto find_spots(Program<T>& program, Variation<T>& variator,
235 const Parameters& params)
236 {
237 vector<float> weights(program.Tree.size());
238
239 std::transform(program.Tree.begin(), program.Tree.end(), weights.begin(),
240 [&](const auto& n){
241 // some nodetypes must always have a weight
242 if (Is<NodeType::OffsetSum>(n.node_type))
243 return 0.0f;
244
245 if (n.get_is_weighted()
246 && IsWeighable(n.node_type))
247 return n.get_prob_change();
248 else
249 return 0.0f;
250 });
251
252 return weights;
253 }
254
255 template<Brush::ProgramType T>
256 static auto mutate(Program<T>& program, Iter spot, Variation<T>& variator,
257 const Parameters& params)
258 {
259 if (spot.node->data.get_is_weighted()==false) // TODO: This condition should never happen. Make sure it dont, then remove it. (this is also true for toggleweighton, also fix that)
260 return false;
261
262 spot.node->data.set_is_weighted(false);
263 return true;
264 }
265};
266
274{
275public:
276 template<Brush::ProgramType T>
277 static auto find_spots(Program<T>& program, Variation<T>& variator,
278 const Parameters& params)
279 {
280 vector<float> weights;
281
282 auto node_map = variator.search_space.node_map;
283
284 if (program.Tree.size() < params.max_size) {
285 Iter iter = program.Tree.begin();
286 std::transform(program.Tree.begin(), program.Tree.end(), std::back_inserter(weights),
287 [&](const auto& n){
288 size_t d = program.Tree.depth(iter);
289 std::advance(iter, 1);
290
291 // we need to make sure there's some node to start the subtree
292 if ((d >= params.max_depth)
293 || (node_map.find(n.ret_type) == node_map.end()) )
294 return 0.0f;
295 else
296 return n.get_prob_change();
297 });
298 }
299 else {
300 weights.resize(program.Tree.size());
301 std::fill(weights.begin(), weights.end(), 0.0f);
302 }
303
304 return weights;
305 }
306
307 template<Brush::ProgramType T>
308 static auto mutate(Program<T>& program, Iter spot, Variation<T>& variator,
309 const Parameters& params)
310 {
311 // check if we exceeded the size/depth constrains (without subtracting,
312 // to avoid overflow cases if the user sets max_size smaller than arity
313 // of smallest operator. The overflow would happen when calculating d and
314 // s in the following lines, to choose the PTC2 limits)
315 if ( params.max_size <= (program.Tree.size() - program.Tree.size(spot))
316 || params.max_depth <= program.Tree.depth(spot) )
317 return false;
318
319 auto spot_type = spot.node->data.ret_type;
320
321 // d and s must be compatible with PTC2 --- they should be based on
322 // tree structure, not program structure
323 size_t d = params.max_depth - program.Tree.depth(spot);
324 size_t s = params.max_size - (program.Tree.size() - program.Tree.size(spot));
325
326 s = r.rnd_int(1, s+1);
327
328 // sample subtree uses PTC2, which operates on depth and size of the tree<Node>
329 // (and not on the program!). we shoudn't care for weights here
330 // cout<< "sampling subtree" << endl;
331 auto subtree = variator.search_space.sample_subtree(spot.node->data, d, s);
332 // cout<< "Finish sampling" << endl;
333
334 if (!subtree) // there is no terminal with compatible arguments
335 return false;
336
337 // cout<< "updating the tree" << endl;
338 // if optional contains a Node, we access its contained value
339 program.Tree.erase_children(spot);
340 // cout<< "erased" << endl;
341
342 program.Tree.move_ontop(spot, subtree.value().begin());
343 // cout<< "replaced" << endl;
344
345 // cout<< "going to return" << endl;
346 return true;
347 }
348};
349
357{
358public:
359 template<Brush::ProgramType T>
360 static auto find_spots(Program<T>& program, Variation<T>& variator,
361 const Parameters& params)
362 {
363 vector<float> weights;
364
365 if (program.Tree.size() < params.get_max_size()) {
366 Iter iter = program.Tree.begin();
367 std::transform(program.Tree.begin(), program.Tree.end(), std::back_inserter(weights),
368 [&](const auto& n){
369 size_t d = 1+program.Tree.depth(iter);
370 std::advance(iter, 1);
371
372 // check if SS holds an operator to avoid failing `check` in sample_op_with_arg
373 if (d >= params.get_max_depth()
374 || variator.search_space.node_map.find(n.ret_type) == variator.search_space.node_map.end()
375 // || check if n.ret_type can be splitted (e.g. DataType::ArrayF)
376 ) {
377 return 0.0f;
378 }
379 else {
380 return n.get_prob_change();
381 }
382 });
383 }
384 else {
385 // fill the vector with zeros, since we're already at max_size
386 weights.resize(program.Tree.size());
387 std::fill(weights.begin(), weights.end(), 0.0f);
388 }
389
390 return weights;
391 }
392
393 template<Brush::ProgramType T>
394 static auto mutate(Program<T>& program, Iter spot, Variation<T>& variator,
395 const Parameters& params)
396 {
397 return false;
398 }
399};
400
424template<Brush::ProgramType T>
425std::tuple<std::optional<Individual<T>>, VectorXf> Variation<T>::cross(
426 const Individual<T>& mom, const Individual<T>& dad)
427{
428 /* subtree crossover between this and other, producing new Program */
429 // choose location by weighted sampling of program
430 // TODO: why doesn't this copy the search space reference to child?
431 Program<T> child(mom.program);
432
433 // pick a subtree to replace
434 vector<float> child_weights(child.Tree.size());
435
436 auto child_iter = child.Tree.begin();
437 std::transform(child.Tree.begin(), child.Tree.end(), child_weights.begin(),
438 [&](const auto& n){
439 auto s_at = child.size_at(child_iter);
440 auto d_at = child.depth_to_reach(child_iter);
441
442 std::advance(child_iter, 1);
443
444 if (s_at<parameters.max_size && d_at<parameters.max_depth)
445 return n.get_prob_change();
446 else
447 return 0.0f;
448 }
449 );
450
451 if (std::all_of(child_weights.begin(), child_weights.end(), [](const auto& w) {
452 return w<=0.0;
453 }))
454 { // There is no spot that has a probability to be selected
455 return std::make_tuple(std::nullopt, VectorXf());
456 }
457
458 // pick a subtree to insert. Selection is based on other_weights
459 Program<T> other(dad.program);
460
461 int attempts = 0;
462 while (++attempts <= 3)
463 {
464 auto child_spot = r.select_randomly(child.Tree.begin(),
465 child.Tree.end(),
466 child_weights.begin(),
467 child_weights.end()
468 );
469
470 auto child_ret_type = child_spot.node->data.ret_type;
471
472 auto allowed_size = parameters.max_size -
473 ( child.size() - child.size_at(child_spot) );
474 auto allowed_depth = parameters.max_depth -
475 ( child.depth_to_reach(child_spot) );
476
477 vector<float> other_weights(other.Tree.size());
478
479 // Iterator to traverse the tree during transformation
480 auto other_iter = other.Tree.begin();
481 std::transform(other.Tree.begin(), other.Tree.end(), other_weights.begin(),
482 [&other, &other_iter, allowed_size, allowed_depth, child_ret_type](const auto& n) mutable {
483 int s = other.size_at(other_iter);
484 int d = other.depth_at(other_iter);
485
486 std::advance(other_iter, 1);
487
488 // Check feasibility and matching return type
489 if (s <= allowed_size && d <= allowed_depth && n.ret_type == child_ret_type) {
490 return n.get_prob_change();
491 }
492
493 return 0.0f; // Non-feasible crossover point
494 }
495 );
496
497 bool matching_spots_found = std::any_of(other_weights.begin(), other_weights.end(),
498 [](float w) { return w > 0.0f; });
499
500 if (matching_spots_found) {
501 // cout << "crossover" << std::endl;
502 VectorXf context = get_context(child, child_spot);
503
504 auto other_spot = r.select_randomly(
505 other.Tree.begin(),
506 other.Tree.end(),
507 other_weights.begin(),
508 other_weights.end()
509 );
510
511 // fmt::print("other_spot : {}\n",other_spot.node->data);
512 // swap subtrees at child_spot and other_spot
513 child.Tree.move_ontop(child_spot, other_spot);
514
515 Individual<T> ind(child);
516 ind.set_variation("cx"); // TODO: use enum here to make it faster
517
518 return std::make_tuple(ind, context);
519 }
520 }
521
522 return std::make_tuple(std::nullopt, VectorXf());
523};
524
558template<Brush::ProgramType T>
559std::tuple<std::optional<Individual<T>>, VectorXf> Variation<T>::mutate(
560 const Individual<T>& parent, string choice)
561{
562 if (choice.empty())
563 {
564 // cout << "Will sample a mut choice" << std::endl;
565 auto options = parameters.mutation_probs;
566
567 bool all_zero = true;
568 for (auto &it : parameters.mutation_probs) {
569 if (it.second > 0.0) {
570 all_zero = false;
571 break;
572 }
573 }
574
575 if (all_zero) { // No mutation can be successfully applied to this solution
576 return std::make_tuple(std::nullopt, VectorXf());
577 }
578
579 // picking a valid mutation option
580 choice = r.random_choice(parameters.mutation_probs);
581 }
582
583 // cout << "Mutation choice: " << choice << std::endl;
584
585 Program<T> copy(parent.program);
586
587 vector<float> weights; // choose location by weighted sampling of program
588 if (choice.compare("point") == 0) // TODO: use enum here to optimize
589 weights = PointMutation::find_spots(copy, (*this), parameters);
590 else if (choice.compare("insert") == 0)
591 weights = InsertMutation::find_spots(copy, (*this), parameters);
592 else if (choice.compare("delete") == 0)
593 weights = DeleteMutation::find_spots(copy, (*this), parameters);
594 else if (choice.compare("subtree") == 0)
595 weights = SubtreeMutation::find_spots(copy, (*this), parameters);
596 else if (choice.compare("toggle_weight_on") == 0)
597 weights = ToggleWeightOnMutation::find_spots(copy, (*this), parameters);
598 else if (choice.compare("toggle_weight_off") == 0)
599 weights = ToggleWeightOffMutation::find_spots(copy, (*this), parameters);
600 else {
601 std::string msg = fmt::format("{} not a valid mutation choice", choice);
603 }
604
605 if (std::all_of(weights.begin(), weights.end(), [](const auto& w) {
606 return w<=0.0;
607 }))
608 { // There is no spot that has a probability to be selected
609 return std::make_tuple(std::nullopt, VectorXf());
610 }
611 VectorXf context = {};
612
613 int attempts = 0;
614 while(++attempts <= 3)
615 {
616 // cout<< "Attempt: " << attempts << std::endl;
617 Program<T> child(parent.program);
618
619 // apply the mutation and check if it succeeded
620 auto spot = r.select_randomly(child.Tree.begin(), child.Tree.end(),
621 weights.begin(), weights.end());
622
623 // Every mutation here works inplace, so they return bool instead of
624 // std::optional to indicare the result of their manipulation over the
625 // program tree. Here we call the mutation function and return the result
626
627 // cout<< "mutate()" << std::endl;
628 context = get_context(child, spot);
629
630 bool success;
631 if (choice.compare("point") == 0)
632 success = PointMutation::mutate(child, spot, (*this), parameters);
633 else if (choice.compare("insert") == 0)
634 success = InsertMutation::mutate(child, spot, (*this), parameters);
635 else if (choice.compare("delete") == 0)
636 success = DeleteMutation::mutate(child, spot, (*this), parameters);
637 else if (choice.compare("subtree") == 0)
638 success = SubtreeMutation::mutate(child, spot, (*this), parameters);
639 else if (choice.compare("toggle_weight_on") == 0)
640 success = ToggleWeightOnMutation::mutate(child, spot, (*this), parameters);
641 else // it must be"toggle_weight_off"
642 success = ToggleWeightOffMutation::mutate(child, spot, (*this), parameters);
643
644 // cout<< "Mutation returned " << success << std::endl;
645 if (success
646 && ( (child.size() <= parameters.max_size)
647 && (child.depth() <= parameters.max_depth) )){
648 // cout<< "Mutation succeeded on attempt " << attempts << std::endl;
649
650 Individual<T> ind(child);
651 // cout<< "new ind program " << child.get_model() << std::endl;
652 ind.set_variation(choice);
653 // cout<< "set variation " << choice << std::endl;
654
655 // subtree performs several samplings, and it will leverate
656 // what point/insert/delete mutations learned about each node utility.
657
658 // TODO: handle subtree - it will sample too many nodes and it may
659 // be hard to track which ones actually improved the expression to
660 // update the bandits/ maybe we should skip it?
661 // mutations that sampled from search space
662 if (choice.compare("point") == 0
663 || choice.compare("insert") == 0
664 || choice.compare("delete") == 0
665 // || choice.compare("subtree") == 0 // TODO: disable this one
666 ) {
667 // cout<< "setting sampled nodes " << spot.node->data.name << std::endl;
668 ind.set_sampled_nodes({spot.node->data});
669 }
670
671 // cout<< "returning..." << std::endl;
672 return std::make_tuple(ind, context);
673 }
674 else { // reseting
675 // cout<< "Mutation failed on attempt " << attempts << std::endl;
676 }
677 }
678
679 return std::make_tuple(std::nullopt, context);
680};
681
682template<Brush::ProgramType T>
683void Variation<T>::vary(Population<T>& pop, int island,
684 const vector<size_t>& parents)
685{
686 auto indices = pop.get_island_indexes(island);
687
688 for (unsigned i = 0; i<indices.size(); ++i)
689 {
690 if (pop.individuals.at(indices.at(i)) != nullptr)
691 {
692 continue; // skipping if it is an individual
693 }
694
695 // pass check for children undergoing variation
696 std::optional<Individual<T>> opt=std::nullopt; // new individual
697
698 const Individual<T>& mom = pop[
699 *r.select_randomly(parents.begin(), parents.end())];
700
701 vector<Individual<T>> ind_parents;
702 VectorXf context = {};
703
704 bool crossover = ( r() < parameters.cx_prob );
705 if (crossover)
706 {
707 const Individual<T>& dad = pop[
708 *r.select_randomly(parents.begin(), parents.end())];
709
710 auto variation_result = cross(mom, dad);
711 ind_parents = {mom, dad};
712 tie(opt, context) = variation_result;
713 }
714 else
715 {
716 // cout << "Performing mutation " << std::endl;
717 auto variation_result = mutate(mom);
718 // cout << "finished mutation" << endl;
719 ind_parents = {mom};
720 tie(opt, context) = variation_result;
721 // cout << "unpacked" << endl;
722 }
723
724 // this assumes that islands do not share indexes before doing variation
725 unsigned id = parameters.current_gen*parameters.pop_size+indices.at(i);
726
727 // mutation and crossover already perform 3 attempts. If it fails, we just fill with a random individual
728
729 Individual<T> ind;
730 if (opt) // variation worked, lets keep this
731 {
732 ind = opt.value();
733 ind.set_parents(ind_parents);
734 }
735 else { // no optional value was returned. creating a new random individual
736 // It seems that the line below will not fix the root in clf programs
737 ind.init(search_space, parameters); // ind.variation is born by default
738
739 // Program<T> p = search_space.make_program<Program<T>>(parameters, 0, 0);
740 // ind = Individual<T>(p);
741 }
742
743 ind.set_objectives(mom.get_objectives()); // it will have an invalid fitness
744
745 ind.is_fitted_ = false;
746 ind.set_id(id);
747
748 // TODO: smarter way of copying the entire fitness
749 // copying mom fitness to the new individual (without making the fitnes valid)
750 // so the bandits can access this information. Fitness will be valid
751 // only when we do set_values(). We are setting these parameters below
752 // because we want to keep the previous values for the bandits, and
753 // we are not updating the fitness values here.
754 ind.fitness.set_loss(mom.fitness.get_loss());
756 ind.fitness.set_size(mom.fitness.get_size());
760
761 // dont set stuff that is not used to calculate the rewards, like crowding_dist
762 // ind.fitness.set_crowding_dist(0.0);
763
764 assert(ind.program.size()>0);
765 assert(ind.fitness.valid()==false);
766
767 pop.individuals.at(indices.at(i)) = std::make_shared<Individual<T>>(ind);
768 }
769};
770
771template <Brush::ProgramType T>
773{
774 // propagate bandits learnt information to the search space.
775 // TODO: not all arms are initialized, if the user set something to zero then we must
776 // disable it. So, during update, we need to properly handle these skipped arms. --> remove this for nodes, allow it just for variations. If the user doesnt want to use a feature or op, he should not set it at the first place. We need to do this with variations because the user
777 // can choose it directly instead of letting brush to figure out.
778
779 // variation: getting new probabilities for variation operators
780 auto variation_probs = variation_bandit.sample_probs(true);
781
782 // cout << "Variation probabilities:" << std::endl;
783 // for (const auto& variation : variation_probs) {
784 // std::cout << " - " << variation.first << ": " << variation.second << std::endl;
785 // }
786
787 if (variation_probs.find("cx") != variation_probs.end())
788 parameters.set_cx_prob(variation_probs.at("cx"));
789
790 for (const auto& variation : variation_probs)
791 if (variation.first != "cx")
792 parameters.mutation_probs[variation.first] = variation.second;
793
794 // terminal: getting new probabilities for terminal nodes in search space
795 for (auto& bandit : terminal_bandits) {
796 auto datatype = bandit.first;
797
798 auto terminal_probs = bandit.second.sample_probs(true);
799 // cout << "Terminal probabilities for datatype " << std::endl;
800 for (auto& terminal : terminal_probs) {
801 // cout << " - " << terminal.first << ": " << terminal.second << std::endl;
802
803 auto terminal_name = terminal.first;
804 auto terminal_prob = terminal.second;
805
806 // Search for the index that matches the terminal name
807 auto it = std::find_if(
808 search_space.terminal_map.at(datatype).begin(),
809 search_space.terminal_map.at(datatype).end(),
810 [&](auto& node) { return node.get_feature() == terminal_name; });
811
812 // if (it != search_space.terminal_map.at(datatype).end()) {
813 auto index = std::distance(search_space.terminal_map.at(datatype).begin(), it);
814
815 // Update the terminal weights with the second value
816 search_space.terminal_weights.at(datatype)[index] = terminal_prob;
817 // }
818 }
819 }
820
821 // operators: getting new probabilities for op nodes
822 for (auto& [ret_type, bandit_map] : op_bandits) {
823 for (auto& [args_type, bandit] : bandit_map) {
824 auto op_probs = bandit.sample_probs(true);
825
826 for (auto& [op_name, op_prob] : op_probs) {
827 // cout << " - " << op_name << ": " << op_prob << std::endl;
828
829 for (const auto& [node_type, node_value]: search_space.node_map.at(ret_type).at(args_type))
830 {
831 // cout << " - Node name: " << node_value.name << std::endl;
832 if (node_value.name == op_name) {
833 // cout << "match" << endl;
834 search_space.node_map_weights.at(ret_type).at(args_type).at(node_type) = op_prob;
835 }
836 }
837 }
838 }
839 }
840
841 // cout << "inside update_ss(). Parameters probs:" << std::endl;
842 // cout << "cx: " << parameters.get_cx_prob() << std::endl;
843 // for (const auto& [name, prob] : parameters.get_mutation_probs())
844 // std::cout << name << ": " << prob << std::endl;
845
846 // cout << "Search Space:" << std::endl;
847 // search_space.print();
848};
849
850} //namespace Var
851} //namespace Brush
void set_variation(string v)
Definition individual.h:112
void set_sampled_nodes(const vector< Node > &nodes)
Definition individual.h:118
void set_id(unsigned i)
Definition individual.h:121
Fitness fitness
aggregate fitness score
Definition individual.h:37
vector< string > get_objectives() const
Definition individual.h:151
void set_objectives(vector< string > objs)
Definition individual.h:152
void set_parents(const vector< Individual< T > > &parents)
Definition individual.h:122
void init(SearchSpace &ss, const Parameters &params)
Definition individual.h:52
Program< T > program
executable data structure
Definition individual.h:17
vector< size_t > get_island_indexes(int island)
Definition population.h:39
vector< std::shared_ptr< Individual< T > > > individuals
Definition population.h:19
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 &params)
insert a node with spot as a child
Definition variation.cpp:48
static auto mutate(Program< T > &program, Iter spot, Variation< T > &variator, const Parameters &params)
Definition variation.cpp:83
static auto find_spots(Program< T > &program, Variation< T > &variator, const Parameters &params)
Definition variation.cpp:51
static auto find_spots(Program< T > &program, Variation< T > &variator, const Parameters &params)
Definition variation.h:679
tree< Node >::pre_order_iterator Iter
Definition variation.h:676
replace node with same typed node
Definition variation.cpp:18
static auto mutate(Program< T > &program, Iter spot, Variation< T > &variator, const Parameters &params)
Definition variation.cpp:21
Inserts an split node in the spot
static auto find_spots(Program< T > &program, Variation< T > &variator, const Parameters &params)
static auto mutate(Program< T > &program, Iter spot, Variation< T > &variator, const Parameters &params)
replaces the subtree rooted in spot
static auto find_spots(Program< T > &program, Variation< T > &variator, const Parameters &params)
static auto mutate(Program< T > &program, Iter spot, Variation< T > &variator, const Parameters &params)
toggle the node's weight OFF
static auto find_spots(Program< T > &program, Variation< T > &variator, const Parameters &params)
static auto mutate(Program< T > &program, Iter spot, Variation< T > &variator, const Parameters &params)
toggle the node's weight ON
static auto mutate(Program< T > &program, Iter spot, Variation< T > &variator, const Parameters &params)
static auto find_spots(Program< T > &program, Variation< T > &variator, const Parameters &params)
Class representing the variation operators in Brush.
Definition variation.h:44
std::optional< Node > bandit_get_node_like(Node node, VectorXf &context)
Definition variation.h:538
Bandit< string > variation_bandit
Definition variation.h:659
std::tuple< std::optional< Individual< T > >, VectorXf > mutate(const Individual< T > &parent, string choice="")
Performs mutation operation on an individual.
std::tuple< std::optional< Individual< T > >, VectorXf > cross(const Individual< T > &mom, const Individual< T > &dad)
Performs croearch_spaceover operation on two individuals.
void vary(Population< T > &pop, int island, const vector< size_t > &parents)
Handles variation of a population.
SearchSpace search_space
Definition variation.h:652
map< DataType, Bandit< string > > terminal_bandits
Definition variation.h:660
std::optional< Node > bandit_sample_terminal(DataType R, VectorXf &context)
Definition variation.h:509
VectorXf get_context(const Program< T > &program, Iter spot)
Definition variation.h:648
map< DataType, map< size_t, Bandit< string > > > op_bandits
Definition variation.h:661
Parameters parameters
Definition variation.h:654
std::optional< Node > bandit_sample_op_with_arg(DataType ret, DataType arg, VectorXf &context, int max_args=0)
Definition variation.h:577
#define HANDLE_ERROR_THROW(err)
Definition error.h:27
static Rnd & r
Definition rnd.h:174
< nsga2 selection operator for getting the front
Definition bandit.cpp:4
auto IsWeighable() noexcept -> bool
Definition node.h:46
float get_loss() const
Definition fitness.h:65
bool valid() const
Definition fitness.h:160
void set_linear_complexity(unsigned int new_lc)
Definition fitness.h:84
void set_complexity(unsigned int new_c)
Definition fitness.h:78
float get_loss_v() const
Definition fitness.h:70
void set_loss_v(float f_v)
Definition fitness.h:68
void set_depth(unsigned int new_d)
Definition fitness.h:90
unsigned int get_complexity() const
Definition fitness.h:81
void set_size(unsigned int new_s)
Definition fitness.h:73
unsigned int get_depth() const
Definition fitness.h:92
unsigned int get_linear_complexity() const
Definition fitness.h:87
unsigned int get_size() const
Definition fitness.h:75
void set_loss(float f)
Definition fitness.h:63
unsigned get_max_size() const
Definition params.h:147
unsigned int max_depth
Definition params.h:37
unsigned int max_size
Definition params.h:38
An individual program, a.k.a. model.
Definition program.h:50
tree< Node > Tree
fitness
Definition program.h:73
int size_at(Iter &top, bool include_weight=true) const
count the size of a given subtree, optionally including the weights in weighted nodes....
Definition program.h:121
int depth() const
count the tree depth of the program. The depth is not influenced by weighted nodes.
Definition program.h:128
int depth_to_reach(Iter &top) const
count the depth until reaching the given subtree. The depth is not influenced by weighted nodes....
Definition program.h:146
int size(bool include_weight=true) const
count the tree size of the program, including the weights in weighted nodes.
Definition program.h:110
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