Brush C++ API
A flexible interpretable machine learning framework
Loading...
Searching...
No Matches
variation.h
Go to the documentation of this file.
1/* Brush
2
3copyright 2020 William La Cava
4license: GNU/GPL v3
5*/
6#ifndef VARIATION_H
7#define VARIATION_H
8
9#include "../ind/individual.h"
10
11#include "../bandit/bandit.h"
13#include "../bandit/dummy.h"
14
15#include "../pop/population.h"
16#include "../eval/evaluation.h"
19
20#include <map>
21#include <optional>
22
23using namespace Brush::Pop;
24using namespace Brush::MAB;
25using namespace Brush::Eval;
26using namespace Brush::Simpl;
27
32namespace Brush {
33namespace Var {
34
43template<ProgramType T>
44class Variation {
45public:
49 Variation() = default;
50
58 : parameters(params)
59 , search_space(ss)
60 , data(d)
61 {
62 init();
63 };
64
69
76 void init(){
77 // initializing variation bandit with the probabilities of non-null variation
78 map<string, float> variation_probs;
79 for (const auto& mutation : parameters.get_mutation_probs())
80 // TODO: I need to figure out a better way of avoiding options that were not positive. Make sure that this does not break the code
81 if (mutation.second > 0.0)
82 variation_probs[mutation.first] = mutation.second;
83
84 if (parameters.cx_prob > 0.0)
85 variation_probs["cx"] = parameters.cx_prob;
86
87 this->variation_bandit = Bandit<string>(parameters.bandit, variation_probs);
88
89 // TODO: should I set C parameter based on pop size or leave it fixed?
90 // TODO: update string comparisons to use .compare method
91 // if (parameters.bandit.compare("dynamic_thompson")==0)
92 // this->variation_bandit.pbandit.set_C(parameters.pop_size);
93
94 // initializing one bandit for each terminal type
95 for (const auto& entry : this->search_space.terminal_weights) {
96 // entry is a tuple <dataType, vector<float>> where the vector is the weights
97
98 // TODO: I dont think we need this find here
99 if (terminal_bandits.find(entry.first) == terminal_bandits.end())
100 {
101 map<string, float> terminal_probs;
102 for (int i = 0; i < entry.second.size(); i++)
103 // TODO: do not insert if coefficient is smaller than zero. Fix bandits to work with that
104 if (entry.second[i] > 0.0)
105 {
106 auto node_name = search_space.terminal_map.at(entry.first).at(i).get_feature();
107 terminal_probs[node_name] = entry.second[i];
108 }
109
110 if (terminal_probs.size()>0)
111 terminal_bandits[entry.first] = Bandit<string>(parameters.bandit,
112 terminal_probs);
113 }
114 }
115
116 // one bandit for each return type. If we look at implementation of
117 // sample op, the thing that matters is the most nested probabilities, so we will
118 // learn only that
119 for (auto& [ret_type, arg_w_map]: search_space.node_map)
120 {
121 // if (op_bandits.find(ret_type) == op_bandits.end())
122 // op_bandits.at(ret_type) = map<size_t, Bandit<string>>();
123
124 for (const auto& [args_type, node_map] : arg_w_map)
125 {
126 // if (op_bandits.at(ret_type).find(args_type) != op_bandits.at(ret_type).end())
127 // continue
128
129 // TODO: this could be made much easier using user_ops
130 map<string, float> node_probs;
131
132 for (const auto& [node_type, node]: node_map)
133 {
134 auto weight = search_space.node_map_weights.at(ret_type).at(args_type).at(node_type);
135
136 if (weight > 0.0)
137 {
138 // Attempt to emplace; if the key exists, do nothing
139 auto [it, inserted] = node_probs.try_emplace(node.name, weight);
140 // cout << node.name << ", " << weight << endl;
141 // If the key already existed, update its value
142 if (!inserted) {
143 it->second = weight;
144 }
145 }
146 }
147
148 if (node_probs.size() > 0)
149 op_bandits[ret_type][args_type] = Bandit<string>(parameters.bandit,
150 node_probs);
151 }
152 }
153
154 inexact_simplifier.initUniformPlanes(128, data.get_training_data().get_n_samples(), 1);
155 // TODO: init simplification with terminals (including booleans?)
156 // for (const auto& entry : this->search_space.terminal_weights) {
157 // map<string, float> terminal_probs;
158 // for (int i = 0; i < entry.second.size(); i++)
159 // if (entry.second[i] > 0.0)
160 // {
161 // Node node = search_space.terminal_map.at(entry.first).at(i);
162
163 // // non-wheightable nodes are not simplified. TODO: revisit this and see if they should (then implement it)
164 // // This is avoiding using booleans.
165 // if (IsWeighable(node.node_type))
166 // {
167 // tree<Node> dummy_tree;
168 // dummy_tree.insert(dummy_tree.begin(), node);
169 // auto it = dummy_tree.begin();
170 // inexact_simplifier.index(it, data.get_training_data());
171 // }
172 // }
173 // }
174 };
175
184 std::tuple<std::optional<Individual<T>>, VectorXf> cross(
185 const Individual<T>& mom, const Individual<T>& dad);
186
194 std::tuple<std::optional<Individual<T>>, VectorXf> mutate(
195 const Individual<T>& parent, string choice="");
196
205 void vary(Population<T>& pop, int island, const vector<size_t>& parents);
206
213 void update_ss();
214
225 void vary_and_update(Population<T>& pop, int island, const vector<size_t>& parents,
226 const Dataset& data, Evaluation<T>& evaluator) {
227
228 // TODO: move implementation to cpp file and keep only declarations here
229 // TODO: rewrite this entire function to avoid repetition (this is a frankenstein)
230 auto indices = pop.get_island_indexes(island);
231
232 vector<std::shared_ptr<Individual<T>>> aux_individuals;
233 for (unsigned i = 0; i < indices.size(); ++i)
234 {
235 if (pop.individuals.at(indices.at(i)) != nullptr)
236 {
237 // cout<< "Skipping individual at index " << indices.at(i) << std::endl;
238 continue; // skipping if it is an individual --- we just want to fill invalid positions
239 }
240
241 // cout<< "------" << endl;
242 // cout<< "Processing individual at index " << indices.at(i) << std::endl;
243
244 // pass check for children undergoing variation
245 std::optional<Individual<T>> opt = std::nullopt; // new individual
246
247 // TODO: should this be randomly selected, or should I use each parent sequentially?
248 // auto idx = *r.select_randomly(parents.begin(), parents.end());
249 auto idx = parents.at(i % parents.size()); // use modulo to cycle through parents
250
251 const Individual<T>& mom = pop[idx];
252
253 // cout<< "orig: " << mom.program.get_model() << endl;
254
255 // if we got here, then the individual is not fully locked and we can proceed with mutation
256 vector<Individual<T>> ind_parents;
257 VectorXf root_context = get_context(mom.program, mom.program.Tree.begin());
258 VectorXf context;
259 string choice;
260
261 // this assumes that islands do not share indexes before doing variation
262 unsigned id = parameters.current_gen * parameters.pop_size + indices.at(i);
263
264 Individual<T> ind; // the new individual
265
266 // fully locked individuals should not be replaced by random ones. returning
267 // a copy
268 if (std::all_of(mom.program.Tree.begin(), mom.program.Tree.end(),
269 [](const auto& n) { return n.get_prob_change()<=0.0; }))
270 {
271 // cout<< "Fully locked individual, copying it" << std::endl;
272 ind = Individual<T>(mom);
273 ind.variation = "born";
274 }
275 else
276 {
277 choice = this->variation_bandit.choose(root_context);
278 if (choice == "cx")
279 {
280 // const Individual<T>& dad = pop[
281 // *r.select_randomly(parents.begin(), parents.end())];
282 const Individual<T>& dad = pop[parents.at((i+1) % parents.size())]; // use modulo to cycle through parents
283
284 // cout<< "Performing crossover" << std::endl;
285 auto variation_result = cross(mom, dad);
286 ind_parents = {mom, dad};
287 tie(opt, context) = variation_result;
288 }
289 else
290 {
291 // cout<< "Performing mutation " << choice << std::endl;
292 auto variation_result = mutate(mom, choice);
293 // cout<< "finished mutation" << endl;
294 ind_parents = {mom};
295 tie(opt, context) = variation_result;
296 // cout<< "unpacked" << endl;
297 }
298
299 if (opt) // variation worked, lets keep this
300 {
301 // cout<< "Variation successful" << std::endl;
302 ind = opt.value();
303 ind.set_parents(ind_parents);
304 }
305 else { // no optional value was returned. creating a new random individual
306 // cout<< "Variation failed, copying the individual" << std::endl;
307
308 ind = Individual<T>(mom);
309 ind.variation = "born";
310
311 // cout<< "Variation failed, creating a new random individual" << std::endl;
312 // ind.init(search_space, parameters); // ind.variation is born by default
313 }
314 }
315
316 // cout<< "Setting objt" << std::endl;
317 // ind.set_objectives(mom.get_objectives()); // it will have an invalid fitness
318
319 ind.set_id(id);
320
321 // cout<< "Setting fitness values" << std::endl;
322 ind.fitness.set_loss(mom.fitness.get_loss());
324 ind.fitness.set_size(mom.fitness.get_size());
328
329 assert(ind.program.size() > 0);
330 assert(ind.fitness.valid() == false);
331
332 // cout<< "Fitting" << std::endl;
333 auto data_aux = data.get_training_data();
334 // cout<< "data aux" << std::endl;
335 // cout<< "program: " << ind.program.get_model() << endl;
336 ind.program.fit(data_aux);
337 // cout<< "done fitting." << std::endl;
338
339 // simplify before calculating fitness (order matters, as they are not refitted and constants simplifier does not replace with the right value.)
340 // TODO: constants_simplifier should set the correct value for the constant (so we dont have to refit).
341 // simplify constants first to avoid letting the lsh simplifier to visit redundant branches
342 // cout<< "after vary: " << ind.program.get_model() << endl;
343 if (parameters.constants_simplification)
344 {
345 constants_simplifier.simplify_tree<T>(ind.program, search_space, data.get_training_data());
346 // cout<< "const: " << ind.program.get_model() << endl;
347 }
348 if (parameters.inexact_simplification)
349 {
350 inexact_simplifier.simplify_tree<T>(ind.program, search_space, data.get_training_data());
351 // cout<< "inext: " << ind.program.get_model() << endl;
352 }
353
354 // cout<< "before assign fit" << endl;
355 evaluator.assign_fit(ind, data, parameters, false);
356 // cout<< "after fit: " << ind.program.get_model() << endl;
357
358 // vector<float> deltas(ind.get_objectives().size(), 0.0f);
359 vector<float> deltas;
360 // cout<< "Updating metrics for individual..." << endl;
361 // cout<< "Objectives: ";
362 // for (const auto& obj : ind.get_objectives()) {
363 // cout << obj << " ";
364 // }
365 // cout << endl;
366
367 float delta = 0.0f;
368 float weight = 0.0f;
369
370 // cout<< "updating metrics" << endl;
371 for (const auto& obj : ind.get_objectives())
372 {
373 // some objectives are unsigned int, which can have weird values if we
374 // do subtractions. Instead, for these cases, we calculate a placeholder
375 // value indicating only if it was greater or not, so we can deal with
376 // this issue.
377
378 // cout<< "Processing objective: " << obj << endl;
379
380 if (obj.compare(parameters.scorer) == 0) {
382 // cout<< " delta (loss) = " << delta << endl;
383 }
384 else if (obj.compare("complexity") == 0) {
385 delta = ind.fitness.get_complexity() > ind.fitness.get_prev_complexity() ? 1.0 : -1.0 ;
386 // cout<< " delta (complexity) = " << delta << endl;
387 }
388 else if (obj.compare("linear_complexity") == 0) {
390 // cout<< " delta (linear_complexity) = " << delta << endl;
391 }
392 else if (obj.compare("size") == 0) {
393 delta = ind.fitness.get_size() > ind.fitness.get_prev_size() ? 1.0 : -1.0;
394 // cout<< " delta (size) = " << delta << endl;
395 }
396 else if (obj.compare("depth") == 0) {
397 delta = ind.fitness.get_depth() > ind.fitness.get_prev_depth() ? 1.0 : -1.0;
398 // cout<< " delta (depth) = " << delta << endl;
399 }
400 else {
401 HANDLE_ERROR_THROW(obj + " is not a known objective");
402 }
403
404 auto it = Individual<T>::weightsMap.find(obj);
405 if (it == Individual<T>::weightsMap.end()) {
406 HANDLE_ERROR_THROW("Weight not found for objective: " + obj);
407 }
408
409 weight = it->second;
410 // cout<< " weight = " << weight << endl;
411
412 float weighted_delta = delta * weight;
413 // cout<< " weighted delta = " << weighted_delta << endl;
414
415 deltas.push_back(weighted_delta);
416 }
417
418 // cout<< "Final deltas vector (size = " << deltas.size() << "): ";
419 // for (const auto& d : deltas) cout << d << " ";
420 // cout << endl;
421
422 bool allPositive = true;
423 bool allNegative = true;
424 for (float d : deltas) {
425 if (d < 0)
426 allPositive = false;
427 if (d > 0)
428 allNegative = false;
429 }
430
431 float r = 0.0;
432 if (allPositive && !allNegative)
433 r = 1.0;
434
435 // linear bandit can handle non-bernoulli-like rewards
436 if (parameters.bandit.compare("linear_thompson") == 0)
437 {
438 // r = std::accumulate(deltas.begin(), deltas.end(), 0.0f,
439 // [](float sum, float delta) {
440 // if (delta > 0) return sum + 1.0f;
441 // // if (delta < 0) return sum - 1.0f;
442 // return sum; // For delta == 0
443 // });
444
445 // if (allPositive)
446 // r = std::accumulate(deltas.begin(), deltas.end(),
447 // 1.0f, std::multiplies<float>());
448
449 if ( allPositive && !allNegative) r = 1.0;
450 if (!allPositive && allNegative) r = -1.0;
451 }
452
453 // cout<< "Updating variation bandit with reward: " << r << std::endl;
454
455 if (ind.get_variation().compare("born") != 0)
456 {
457 // cout<< "Updating variation bandit with variation: " << ind.get_variation() << " and reward: " << r << ". choosen variation was: " << choice << std::endl;
458 this->variation_bandit.update(ind.get_variation(), r, root_context);
459
460 // if (!ind.get_variation().compare("born") && !ind.get_variation().compare("cx")
461 // && !ind.get_variation().compare("subtree"))
462 // {
463 if (ind.get_sampled_nodes().size() > 0) {
464 // cout<< "Updating terminal and operator bandits for sampled nodes" << std::endl;
465 const auto& changed_nodes = ind.get_sampled_nodes();
466 for (auto& node : changed_nodes) {
467 if (node.get_arg_count() == 0) {
468 auto datatype = node.get_ret_type();
469 // cout<< "Updating terminal bandit for node: " << node.name << std::endl;
470 this->terminal_bandits[datatype].update(node.get_feature(), r, context);
471 }
472 else {
473 auto ret_type = node.get_ret_type();
474 auto args_type = node.args_type();
475 auto name = node.name;
476 // cout<< "Updating operator bandit for node: " << name << std::endl;
477 this->op_bandits[ret_type][args_type].update(name, r, context);
478 }
479 }
480 }
481 // }
482 }
483 else
484 { // giving zero reward if the variation failed
485 // cout<< "Variation failed, updating variation bandit with choice: " << choice << " and reward: 0.0" << std::endl;
486 this->variation_bandit.update(choice, 0.0, root_context);
487 }
488
489 // aux_individuals.push_back(std::make_shared<Individual<T>>(ind));
490 pop.individuals.at(indices.at(i)) = std::make_shared<Individual<T>>(ind);
491 // cout<< "Individual at index " << indices.at(i) << " updated successfully" << std::endl;
492 }
493
494 // updating the population with the new individual
495 // int aux_index = 0;
496 // for (unsigned i = 0; i < indices.size(); ++i)
497 // {
498 // if (pop.individuals.at(indices.at(i)) != nullptr)
499 // {
500 // // the nullptrs should be at the end of the vector
501 // pop.individuals.at(indices.at(i)) = aux_individuals.at(aux_index);
502 // aux_index++;
503 // }
504 // }
505 }
506
507 // these functions below will extract context and use it to choose the nodes to replace
508 // bandit_sample_terminal
509 std::optional<Node> bandit_sample_terminal(DataType R, VectorXf& context)
510 {
511 // cout << "bandit_sample_terminal called with DataType: " << std::endl;
512
513 if (terminal_bandits.find(R) == terminal_bandits.end()) {
514 // cout << "No bandit found for DataType: " << std::endl;
515 return std::nullopt;
516 }
517
518 auto& bandit = terminal_bandits.at(R);
519 string terminal_name = bandit.choose(context);
520 // cout << "Bandit chose terminal name: " << terminal_name << std::endl;
521
522 auto it = std::find_if(
523 search_space.terminal_map.at(R).begin(),
524 search_space.terminal_map.at(R).end(),
525 [&](auto& node) { return node.get_feature() == terminal_name; });
526
527 if (it != search_space.terminal_map.at(R).end()) {
528 auto index = std::distance(search_space.terminal_map.at(R).begin(), it);
529 // cout << "Terminal found at index: " << index << std::endl;
530 return search_space.terminal_map.at(R).at(index);
531 }
532
533 // cout << "Terminal not found for name: " << terminal_name << std::endl;
534 return std::nullopt;
535 };
536
537 // bandit_get_node_like
538 std::optional<Node> bandit_get_node_like(Node node, VectorXf& context)
539 {
540 // cout << "bandit_get_node_like called with node: " << node.name << std::endl;
541
542 // TODO: use search_space.terminal_types here (and in search_space get_node_like as well)
544 // cout << "Node is of type Terminal, Constant, or MeanLabel" << std::endl;
545 return bandit_sample_terminal(node.ret_type, context);
546 }
547
548 if (op_bandits.find(node.ret_type) == op_bandits.end()) {
549 // cout << "No bandit found for return type: " << std::endl;
550 return std::nullopt;
551 }
552 if (op_bandits.at(node.ret_type).find(node.args_type()) == op_bandits.at(node.ret_type).end()) {
553 // cout << "No bandit found for arg type: " << std::endl;
554 return std::nullopt;
555 }
556
557 auto& bandit = op_bandits[node.ret_type][node.args_type()];
558 string node_name = bandit.choose(context);
559 // cout << "Bandit chose node name: " << node_name << std::endl;
560
561 auto entries = search_space.node_map[node.ret_type][node.args_type()];
562 // cout << "Ret match size: " << entries.size() << std::endl;
563
564 for (const auto& [node_type, node_value]: entries)
565 {
566 // cout << " - Node name: " << node_value.name << std::endl;
567 if (node_value.name == node_name) {
568 // cout << "Node name match: " << node_value.name << std::endl;
569 return node_value;
570 }
571 }
572
573 return std::nullopt;
574 };
575
576 // bandit_sample_op_with_arg
577 std::optional<Node> bandit_sample_op_with_arg(DataType ret, DataType arg,
578 VectorXf& context, int max_args=0)
579 {
580 auto args_map = search_space.node_map.at(ret);
581 vector<size_t> matches;
582 vector<float> weights;
583
584 for (const auto& [args_type, name_map]: args_map) {
585 for (const auto& [name, node]: name_map) {
586 auto node_arg_types = node.get_arg_types();
587
588 auto within_size_limit = !(max_args) || (node.get_arg_count() <= max_args);
589
590 if (in(node_arg_types, arg)
591 && within_size_limit
592 && search_space.node_map_weights.at(ret).at(args_type).at(name) > 0.0f ) {
593 // if it can be sampled
594 matches.push_back(node.args_type());
595 }
596 }
597 }
598
599 if (matches.size()==0)
600 return std::nullopt;
601
602 // we randomly select args type. This is what determines which bandit to use
603 auto args_type = *r.select_randomly(matches.begin(),
604 matches.end() );
605 auto& bandit = op_bandits[ret][args_type];
606 string node_name = bandit.choose(context);
607
608 // TODO: this could be more efficient
609 auto entries = search_space.node_map[ret][args_type];
610 for (const auto& [node_type, node_value]: entries)
611 {
612 if (node_value.name == node_name) {
613 return node_value;
614 }
615 }
616
617 return std::nullopt;
618 };
619
620 // bandit_sample_op
621 std::optional<Node> bandit_sample_op(DataType ret, VectorXf& context)
622 {
623 if (search_space.node_map.find(ret) == search_space.node_map.end())
624 return std::nullopt;
625
626 // any bandit to do the job
627 auto& [args_type, bandit] = *r.select_randomly(op_bandits[ret].begin(),
628 op_bandits[ret].end() );
629
630 string node_name = bandit.choose(context);
631
632 auto entries = search_space.node_map[ret][args_type];
633 for (const auto& [node_type, node_value]: entries)
634 {
635 if (node_value.name == node_name) {
636 return node_value;
637 }
638 }
639
640 return std::nullopt;
641 };
642
643 // bandit_sample_subtree // TODO: should I implement this? (its going to be hard).
644 // without this one being performed directly by the bandits, we then rely on
645 // the sampled probabilities we update after every generation. Since there are lots
646 // of samplings, I think it is ok to not update them and just use the distribution they learned.
647
648 VectorXf get_context(const Program<T>& program, Iter spot) {
649 return variation_bandit.get_context<T>(program, spot, search_space, data); }
650
651 // they need to be references because we are going to modify them
652 SearchSpace search_space; // The search space for the variation operator.
653 Dataset& data; // the data used to extract context and evaluate the models
654 Parameters parameters; // The parameters for the variation operator
655private:
656 // bandits will internaly work as an interface between variation and its searchspace.
657 // they will sample from the SS (instead of letting the search space do it directly),
658 // and also propagate what they learn back to the search space at the end of the execution.
660 map<DataType, Bandit<string>> terminal_bandits;
661 map<DataType, map<size_t, Bandit<string>>> op_bandits;
662
663 // simplification methods
666};
667
668// // Explicitly instantiate the template for brush program types
669// template class Variation<ProgramType::Regressor>;
670// template class Variation<ProgramType::BinaryClassifier>;
671// template class Variation<ProgramType::MulticlassClassifier>;
672// template class Variation<ProgramType::Representer>;
673
675public:
676 using Iter = tree<Node>::pre_order_iterator;
677
678 template<Brush::ProgramType T>
679 static auto find_spots(Program<T>& program, Variation<T>& variator,
680 const Parameters& params)
681 {
682 vector<float> weights(program.Tree.size());
683
684 // by default, mutation can happen anywhere, based on node weights
685 std::transform(program.Tree.begin(), program.Tree.end(), weights.begin(),
686 [&](const auto& n){ return n.get_prob_change();});
687
688 // Must have same size as tree, even if all weights <= 0.0
689 return weights;
690 }
691
692 template<Brush::ProgramType T>
693 static auto mutate(Program<T>& program, Iter spot, Variation<T>& variator,
694 const Parameters& params);
695};
696
697} //namespace Var
698} //namespace Brush
699#endif
holds variable type data.
Definition data.h:51
Class for evaluating the fitness of individuals in a population.
Definition evaluation.h:27
void assign_fit(Individual< T > &ind, const Dataset &data, const Parameters &params, bool val=false)
Assign fitness to an individual.
static std::map< std::string, float > weightsMap
set parent ids using id values
Definition individual.h:138
void set_id(unsigned i)
Definition individual.h:121
vector< Node > get_sampled_nodes() const
Definition individual.h:119
Fitness fitness
aggregate fitness score
Definition individual.h:37
string get_variation() const
Definition individual.h:113
vector< string > get_objectives() const
Definition individual.h:151
void set_parents(const vector< Individual< T > > &parents)
Definition individual.h:122
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
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
static auto mutate(Program< T > &program, Iter spot, 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.
void init()
Initializes the Variation object with parameters and search space.
Definition variation.h:76
Variation()=default
Default constructor.
Inexact_simplifier inexact_simplifier
Definition variation.h:665
SearchSpace search_space
Definition variation.h:652
~Variation()
Destructor.
Definition variation.h:68
map< DataType, Bandit< string > > terminal_bandits
Definition variation.h:660
std::optional< Node > bandit_sample_terminal(DataType R, VectorXf &context)
Definition variation.h:509
Constants_simplifier constants_simplifier
Definition variation.h:664
VectorXf get_context(const Program< T > &program, Iter spot)
Definition variation.h:648
void vary_and_update(Population< T > &pop, int island, const vector< size_t > &parents, const Dataset &data, Evaluation< T > &evaluator)
Varies a population and updates the selection strategy based on rewards.
Definition variation.h:225
map< DataType, map< size_t, Bandit< string > > > op_bandits
Definition variation.h:661
Variation(Parameters &params, SearchSpace &ss, Dataset &d)
Constructor that initializes the Variation object with parameters and search space.
Definition variation.h:57
Parameters parameters
Definition variation.h:654
std::optional< Node > bandit_sample_op(DataType ret, VectorXf &context)
Definition variation.h:621
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
bool in(const V &v, const T &i)
check if element is in vector.
Definition utils.h:192
static Rnd & r
Definition rnd.h:174
< nsga2 selection operator for getting the front
Definition bandit.cpp:4
DataType
data types.
Definition types.h:143
auto Is(NodeType nt) -> bool
Definition node.h:272
tree< Node >::pre_order_iterator Iter
Definition program.h:37
float get_loss() const
Definition fitness.h:65
unsigned int get_prev_depth() const
Definition fitness.h:93
float get_prev_loss() const
Definition fitness.h:66
unsigned int get_prev_size() const
Definition fitness.h:76
unsigned int get_prev_complexity() const
Definition fitness.h:82
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_prev_linear_complexity() const
Definition fitness.h:88
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
The Bandit struct represents a multi-armed bandit.
Definition bandit.h:34
class holding the data for a node in a tree.
Definition node.h:84
NodeType node_type
the node type
Definition node.h:95
DataType ret_type
return data type
Definition node.h:101
std::size_t args_type() const
Definition node.h:180
An individual program, a.k.a. model.
Definition program.h:50
tree< Node > Tree
fitness
Definition program.h:73
Program< PType > & fit(const Dataset &d)
Definition program.h:150
int size(bool include_weight=true) const
count the tree size of the program, including the weights in weighted nodes.
Definition program.h:110
Holds a search space, consisting of operations and terminals and functions, and methods to sample tha...