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