Ultra 0.9.4
Loading...
Searching...
No Matches
oracle.tcc
Go to the documentation of this file.
1
12
13#if !defined(ULTRA_ORACLE_H)
14# error "Don't include this file directly, include the specific .h instead"
15#endif
16
17#if !defined(ULTRA_ORACLE_TCC)
18#define ULTRA_ORACLE_TCC
19
20// ***********************************************************************
21// * basic_reg_oracle *
22// ***********************************************************************
23
24template<Individual P, bool S>
25const std::string basic_reg_oracle<P, S>::SERIALIZE_ID(
26 gp::Team<P> ? "TEAM_REG_ORACLE" : "REG_ORACLE");
27
31template<Individual P, bool S>
32basic_reg_oracle<P, S>::basic_reg_oracle(const P &prg)
33 : internal::reg_oracle_storage<P, S>(prg)
34{
35 Expects(!prg.empty());
36
37 Ensures(is_valid());
38}
39
44template<Individual P, bool S>
45basic_reg_oracle<P, S>::basic_reg_oracle(std::istream &in,
46 const symbol_set &ss)
47 : internal::reg_oracle_storage<P, S>(in, ss)
48{
49 static_assert(S, "reg_oracle requires storage for de-serialization");
50
51 Ensures(is_valid());
52}
53
58template<Individual P, bool S>
59value_t basic_reg_oracle<P, S>::operator()(const std::vector<value_t> &e) const
60{
61 if constexpr (!gp::Team<P>)
62 return this->run(e);
63 else
64 {
65 D_DOUBLE sum(0), count(0);
66
67 for (const auto &core : this->team_)
68 {
69 const auto res(core.run(e));
70
71 if (has_value(res))
72 {
73 sum += std::get<D_DOUBLE>(res);
74 ++count;
75 }
76 }
77
78 if (count > 0.0)
79 return sum / count;
80
81 return {};
82 }
83}
84
91template<Individual P, bool S>
92classification_result basic_reg_oracle<P, S>::tag(
93 const std::vector<value_t> &) const
94{
95 throw std::logic_error("tag() called on regression oracle");
96}
97
102template<Individual P, bool S>
103std::string basic_reg_oracle<P, S>::name(const value_t &a) const
104{
105 return lexical_cast<std::string>(a);
106}
107
111template<Individual P, bool S>
112bool basic_reg_oracle<P, S>::is_valid() const
113{
114 return internal::reg_oracle_storage<P, S>::is_valid();
115}
116
123template<Individual P, bool S>
124bool basic_reg_oracle<P, S>::save(std::ostream &out) const
125{
126 return internal::reg_oracle_storage<P, S>::save(out);
127}
128
129// ***********************************************************************
130// * reg_oracle_adaptor *
131// ***********************************************************************
132
136template<RegressionPredictor O>
137reg_oracle_adaptor<O>::reg_oracle_adaptor(O oracle)
138 : oracle_(std::move(oracle))
139{
140}
141
150template<RegressionPredictor O>
151value_t reg_oracle_adaptor<O>::operator()(const std::vector<value_t> &x) const
152{
153 return oracle_(x);
154}
155
162template<RegressionPredictor O>
163std::string reg_oracle_adaptor<O>::name(const value_t &v) const
164{
165 return lexical_cast<std::string>(v);
166}
167
173template<RegressionPredictor O>
174classification_result reg_oracle_adaptor<O>::tag(
175 const std::vector<value_t> &) const
176{
177 throw std::logic_error("tag() called on regression oracle");
178}
179
187template<RegressionPredictor O>
188std::string reg_oracle_adaptor<O>::serialize_id() const
189{
190 return "REG_ORACLE_ADAPTOR";
191}
192
198template<RegressionPredictor O>
199bool reg_oracle_adaptor<O>::save(std::ostream &) const
200{
201 throw std::logic_error("reg_oracle_adaptor is not serializable");
202}
203
204// ***********************************************************************
205// * basic_gaussian_oracle *
206// ***********************************************************************
207
208template<class I, bool S>
209const std::string basic_gaussian_oracle<I, S>::SERIALIZE_ID(
210 "GAUSSIAN_ORACLE");
211
217template<class I, bool S>
218basic_gaussian_oracle<I, S>::basic_gaussian_oracle(
219 const I &ind, const dataframe &d, std::vector<std::string> names)
220 : basic_class_oracle(std::move(names)), oracle_(ind), gauss_dist_(d.classes())
221{
222 static_assert(Individual<I>);
223 Expects(!ind.empty());
224 Expects(d.classes() > 1);
225
226 fill_vector(d);
227
228 Ensures(is_valid());
229}
230
237template<class I, bool S>
238basic_gaussian_oracle<I, S>::basic_gaussian_oracle(std::istream &in,
239 const symbol_set &ss)
240 : basic_class_oracle(), oracle_(in, ss)
241{
242 static_assert(
243 S, "gaussian_oracle requires storage space for de-serialization");
244
245 std::size_t n;
246 if (!(in >> n))
247 throw exception::data_format("Cannot read gaussian_oracle size component");
248
249 for (std::size_t i(0); i < n; ++i)
250 {
251 distribution<double> d;
252 if (!d.load(in))
253 throw exception::data_format(
254 "Cannot read gaussian_oracle distribution component");
255
256 gauss_dist_.push_back(d);
257 }
258
259 if (!load_names(in))
260 throw exception::data_format(
261 "Cannot read gaussian_oracle class_names component");
262
263 Ensures(is_valid());
264}
265
304template<class I, bool S>
305void basic_gaussian_oracle<I, S>::fill_vector(const dataframe &d)
306{
307 Expects(d.classes() > 1);
308
309 std::vector<unsigned> unknown_in_class(d.classes(), 0);
310
311 constexpr double cut(100000000.0);
312
313 for (const auto &ex : d)
314 {
315 const auto cl(label(ex));
316
317 if (const auto res(oracle_(ex.input)); has_value(res))
318 {
319 auto val(std::get<D_DOUBLE>(res));
320 val = std::clamp(val, -cut, cut);
321
322 gauss_dist_[cl].add(val);
323 }
324 else
325 ++unknown_in_class[cl];
326 }
327
328 constexpr double min_variance(1e-6);
329 constexpr unsigned missing_per_inflation(2);
330
331 for (std::size_t cl(0); cl < d.classes(); ++cl)
332 {
333 auto &g(gauss_dist_[cl]);
334
335 if (const unsigned k(unknown_in_class[cl] / missing_per_inflation);
336 g.size() > 0)
337 {
338 const double mean(g.mean());
339 const double var(std::max(g.variance(), min_variance));
340 const double delta(2.0 * std::sqrt(var));
341
342 for (std::size_t i(0); i < k; ++i)
343 {
344 g.add(mean + delta);
345 g.add(mean - delta);
346 }
347 }
348 else // no valid outputs: maximally uninformative distribution
349 {
350 for (std::size_t i(0); i < std::max(1u, k); ++i)
351 {
352 g.add(+cut);
353 g.add(-cut);
354 }
355 }
356
357 Ensures(g.size() > 0);
358 Ensures(std::isfinite(g.mean()));
359 Ensures(g.variance() >= 0.0);
360 Ensures(std::isfinite(g.variance()));
361 }
362}
363
436template<class I, bool S>
437classification_result basic_gaussian_oracle<I, S>::tag(
438 const std::vector<value_t> &ex) const
439{
440 const std::size_t classes(gauss_dist_.size());
441
442 if (classes == 1)
443 return {0, 1.0};
444
445 const auto res(oracle_(ex));
446
447 // Missing inference: fall back to class priors.
448 if (!has_value(res))
449 {
450 std::size_t best(0);
451 double best_count(0.0), total(0.0);
452
453 for (std::size_t i(0); i < classes; ++i)
454 {
455 // Effective sample count, including variance inflation.
456 const auto n(static_cast<double>(gauss_dist_[i].size()));
457 total += n;
458
459 if (n > best_count)
460 {
461 best_count = n;
462 best = i;
463 }
464 }
465
466 const double confidence(total > 0.0 ? best_count / total : 0.0);
467 return {best, confidence};
468 }
469
470 // Normal likelihood-based inference.
471 const auto x(std::get<D_DOUBLE>(res));
472
473 double best_p(0.0), sum_p(0.0);
474 src::class_t probable_class(0);
475
476 for (std::size_t i(0); i < classes; ++i)
477 {
478 const auto mean(gauss_dist_[i].mean());
479 const auto variance(gauss_dist_[i].variance());
480 const auto distance(std::fabs(x - mean));
481
482 double p(0.0);
483 if (issmall(variance)) // degenerate or nearly-degenerate
484 { // distribution: treat as a point
485 p = issmall(distance) ? 1.0 : 0.0; // mass at the mean
486 }
487 else // standard case
488 p = std::exp(-distance * distance / variance);
489 // std::exp(-0.5 * distance * distance / variance)
490 // / std::sqrt(2.0 * std::numbers::pi_v<double> * variance);
491
492 if (p > best_p)
493 {
494 best_p = p;
495 probable_class = i;
496 }
497
498 sum_p += p;
499 }
500
501 // Normalized confidence value.
502 // Do not change `sum_p > 0.0` with
503 // - `issmall(sum_p)` => when `sum_p` is small, `best_p` is smaller and the
504 // division works well.
505 // - `sum_p` => it's the same thing but will produce a warning with
506 // `-Wfloat-equal`
507 const double confidence(sum_p > 0.0 ? best_p / sum_p : 0.0);
508
509 return {probable_class, confidence};
510}
511
518template<class I, bool S>
519bool basic_gaussian_oracle<I, S>::save(std::ostream &out) const
520{
521 if (!oracle_.save(out))
522 return false;
523
524 if (!(out << gauss_dist_.size() << '\n'))
525 return false;
526
527 for (const auto &g : gauss_dist_)
528 if (!g.save(out))
529 return false;
530
531 return save_names(out);
532}
533
537template<class I, bool S>
538bool basic_gaussian_oracle<I, S>::is_valid() const
539{
540 return oracle_.is_valid() && !gauss_dist_.empty();
541}
542
543// ***********************************************************************
544// * basic_binary_oracle *
545// ***********************************************************************
546
547template<class I, bool S>
548const std::string basic_binary_oracle<I, S>::SERIALIZE_ID(
549 "BINARY_ORACLE");
550
556template<class I, bool S>
557basic_binary_oracle<I, S>::basic_binary_oracle(
558 const I &ind, [[maybe_unused]] const dataframe &d,
559 std::vector<std::string> names)
560 : basic_class_oracle(std::move(names)), oracle_(ind)
561{
562 static_assert(Individual<I>);
563 Expects(!ind.empty());
564 Expects(d.classes() == 2);
565
566 Ensures(is_valid());
567}
568
575template<class I, bool S>
576basic_binary_oracle<I, S>::basic_binary_oracle(std::istream &in,
577 const symbol_set &ss)
578 : basic_class_oracle(), oracle_(in, ss)
579{
580 static_assert(
581 S, "binary_oracle requires storage space for de-serialization");
582
583 if (!load_names(in))
584 throw exception::data_format(
585 "Cannot read binary_oracle class_names component");
586
587 Ensures(is_valid());
588}
589
595template<class I, bool S>
596classification_result basic_binary_oracle<I, S>::tag(
597 const std::vector<value_t> &ex) const
598{
599 const auto res(oracle_(ex));
600 const double val(has_value(res) ? std::get<D_DOUBLE>(res) : 0.0);
601
602 return {val > 0.0 ? 1u : 0u,
603 std::fabs(2.0 * std::numbers::inv_pi * std::atan(val))};
604}
605
609template<class I, bool S>
610bool basic_binary_oracle<I, S>::is_valid() const
611{
612 return oracle_.is_valid();
613}
614
621template<class I, bool S>
622bool basic_binary_oracle<I, S>::save(std::ostream &out) const
623{
624 if (!oracle_.save(out))
625 return false;
626
627 return save_names(out);
628}
629
630// ***********************************************************************
631// * class_oracle_adaptor *
632// ***********************************************************************
633
638template<ClassificationPredictor O>
639class_oracle_adaptor<O>::class_oracle_adaptor(O oracle,
640 std::vector<std::string> names)
641 : basic_class_oracle(std::move(names)), oracle_(std::move(oracle))
642{
643}
644
653template<ClassificationPredictor O>
654classification_result class_oracle_adaptor<O>::tag(
655 const std::vector<value_t> &x) const
656{
657 return oracle_.tag(x);
658}
659
667template<ClassificationPredictor O>
668std::string class_oracle_adaptor<O>::serialize_id() const
669{
670 return "CLASS_ORACLE_ADAPTOR";
671}
672
678template<ClassificationPredictor O>
679bool class_oracle_adaptor<O>::save(std::ostream &) const
680{
681 throw std::logic_error("class_oracle_adaptor is not serializable");
682}
683
684// ***********************************************************************
685// * team_class_oracle *
686// ***********************************************************************
687
688template<ClassificationPredictor O, team_composition C>
689const std::string team_class_oracle<O, C>::SERIALIZE_ID(
690 "TEAM_" + O::SERIALIZE_ID);
691
697template<ClassificationPredictor O, team_composition C>
698template<Individual I>
700 const gp::team<I> &t, const dataframe &d, std::vector<std::string> names)
701 : basic_class_oracle(std::move(names)), classes_(d.classes())
702{
703 team_.reserve(t.size());
704 for (const auto &ind : t)
705 team_.emplace_back(ind, d);
706}
707
714template<ClassificationPredictor O, team_composition C>
716 const symbol_set &ss)
718{
719 if (!(in >> classes_))
720 throw exception::data_format("Cannot read number of classes");
721
722 std::size_t s;
723 if (!(in >> s))
724 throw exception::data_format("Cannot read team size");
725
726 team_.reserve(s);
727 for (std::size_t i(0); i < s; ++i)
728 team_.emplace_back(in, ss);
729
730 if (!load_names(in))
731 throw exception::data_format("Cannot read class_names");
732}
733
750template<ClassificationPredictor O, team_composition C>
751classification_result team_class_oracle<O, C>::tag(
752 const std::vector<value_t> &instance) const
753{
754 Expects(team_.size());
755
756 if constexpr (C == team_composition::wta)
757 {
758 const auto size(team_.size());
759 auto best(team_[0].tag(instance));
760
761 for (std::size_t i(1); i < size; ++i)
762 {
763 const auto res(team_[i].tag(instance));
764
765 if (res.sureness > best.sureness)
766 best = res;
767 }
768
769 return best;
770 }
771 else if constexpr (C == team_composition::mv)
772 {
773 std::vector<unsigned> votes(classes_);
774
775 for (const auto &oracle : team_)
776 ++votes[oracle.tag(instance).label];
777
778 src::class_t max(0);
779 for (auto i(max + 1); i < classes_; ++i)
780 if (votes[i] > votes[max])
781 max = i;
782
783 return {max, static_cast<double>(votes[max])
784 / static_cast<double>(team_.size())};
785 }
786}
787
794template<ClassificationPredictor O, team_composition C>
795bool team_class_oracle<O, C>::save(std::ostream &out) const
796{
797 if (!(out << classes_ << '\n'))
798 return false;
799
800 if (!(out << team_.size() << '\n'))
801 return false;
802
803 for (const auto &i : team_)
804 if (!i.save(out))
805 return false;
806
807 return save_names(out);
808}
809
813template<ClassificationPredictor O, team_composition C>
814std::string team_class_oracle<O, C>::serialize_id() const noexcept
815{
816 return SERIALIZE_ID;
817}
818
822template<ClassificationPredictor O, team_composition C>
824{
825 return classes_ > 1;
826}
827
828namespace serialize::oracle
829{
830
831namespace internal
832{
833using build_func = std::unique_ptr<basic_oracle> (*)(std::istream &,
834 const symbol_set &);
835
836template<class U>
837std::unique_ptr<basic_oracle> build(std::istream &in, const symbol_set &ss)
838{
839 return std::make_unique<U>(in, ss);
840}
841
842extern std::map<std::string, build_func> factory_;
843} // namespace internal
844
848template<class U>
849bool insert(const std::string &id)
850{
851 Expects(!id.empty());
852 return internal::factory_.insert({id, internal::build<U>}).second;
853}
854
855template<Individual T>
856std::unique_ptr<basic_oracle> load(std::istream &in, const symbol_set &ss)
857{
858 if (internal::factory_.find(reg_oracle<T>::SERIALIZE_ID)
859 == internal::factory_.end())
860 {
861 insert<reg_oracle<T>>(reg_oracle<T>::SERIALIZE_ID);
862 //insert<dyn_slot_lambda_f<T>>(dyn_slot_lambda_f<T>::SERIALIZE_ID);
863 insert<gaussian_oracle<T>>(gaussian_oracle<T>::SERIALIZE_ID);
864 insert<binary_oracle<T>>(binary_oracle<T>::SERIALIZE_ID);
865 }
866
867 std::string id;
868 if (!(in >> id))
869 return nullptr;
870
871 const auto iter(internal::factory_.find(id));
872 if (iter != internal::factory_.end())
873 return iter->second(in, ss);
874
875 return nullptr;
876}
877
878} // namespace serialize::oracle
879
880#endif // include guard
Definition exceptions.h:30
Definition team.h:72
std::size_t size() const noexcept
Definition team.h:246
Definition oracle.h:204
basic_class_oracle(std::vector< std::string >={})
Definition oracle.cc:23
bool load_names(std::istream &)
Loads the class names, accepting a zero count for omitted names.
Definition oracle.cc:57
Definition dataframe.h:76
team_class_oracle(const gp::team< I > &, const dataframe &, std::vector< std::string >={})
Definition oracle.h:700
bool is_valid() const final
Definition oracle.h:824
classification_result tag(const std::vector< value_t > &) const final
Definition oracle.h:752
Definition symbol_set.h:115
class_t label(const example &e)
Definition dataframe.cc:87
double distance(F f1, F f2)
Definition fitness.tcc:117
bool load(std::istream &in, F *f)
Definition fitness.tcc:190
STL namespace.
value_t run(const gp::individual &ind)
Definition interpreter.cc:239
bool has_value(const value_t &v) noexcept
Definition value.cc:31
std::variant< D_VOID, D_INT, D_DOUBLE, D_STRING, const D_NULLARY *, D_ADDRESS, const D_VARIABLE *, D_IVECTOR > value_t
Definition value.h:54
bool insert(const std::string &id)
Definition oracle.tcc:849