Vita
lambda_f.tcc
1/**
2 * \file
3 * \remark This file is part of VITA.
4 *
5 * \copyright Copyright (C) 2013-2020 EOS di Manlio Morini.
6 *
7 * \license
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
10 * You can obtain one at http://mozilla.org/MPL/2.0/
11 */
12
13#if !defined(VITA_LAMBDA_F_H)
14# error "Don't include this file directly, include the specific .h instead"
15#endif
16
17#if !defined(VITA_LAMBDA_F_TCC)
18#define VITA_LAMBDA_F_TCC
19
20template<class T, bool S>
21const std::string basic_reg_lambda_f<T, S>::SERIALIZE_ID(
22 is_team<T>() ? "TEAM_REG_LAMBDA_F" : "REG_LAMBDA_F");
23
24template<class T, bool S, bool N>
25const std::string basic_dyn_slot_lambda_f<T, S, N>::SERIALIZE_ID(
26 "DYN_SLOT_LAMBDA_F");
27
28template<class T, bool S, bool N>
29const std::string basic_gaussian_lambda_f<T, S, N>::SERIALIZE_ID(
30 "GAUSSIAN_LAMBDA_F");
31
32template<class T, bool S, bool N>
33const std::string basic_binary_lambda_f<T, S, N>::SERIALIZE_ID(
34 "BINARY_LAMBDA_F");
35
36template<class T, bool S, bool N, template<class, bool, bool> class L,
37 team_composition C>
38const std::string team_class_lambda_f<T, S, N, L, C>::SERIALIZE_ID(
39 "TEAM_" + L<T, S, N>::SERIALIZE_ID);
40
41///
42/// \param[in] prg the program (individual/team) to be lambdified
43///
44template<class T, bool S>
45basic_reg_lambda_f<T, S>::basic_reg_lambda_f(const T &prg)
46 : detail::reg_lambda_f_storage<T, S>(prg)
47{
48 Ensures(is_valid());
49}
50
51///
52/// \param[in] in input stream
53/// \param[in] ss active symbol set
54///
55template<class T, bool S>
56basic_reg_lambda_f<T, S>::basic_reg_lambda_f(std::istream &in,
57 const symbol_set &ss)
58 : detail::reg_lambda_f_storage<T, S>(in, ss)
59{
60 static_assert(S, "reg_lambda_f requires storage for de-serialization");
61
62 Ensures(is_valid());
63}
64
65///
66/// \param[in] e input example for the lambda function
67/// \return the output value associated with `e`
68///
69template<class T, bool S>
70value_t basic_reg_lambda_f<T, S>::operator()(const dataframe::example &e) const
71{
72 // Here using *tag dispatching by instance*: main function delegates to an
73 // implementation function that receives standard arguments plus a dummy
74 // argument based on a compile-time condition.
75 // Usually this is much easier to debug and get right that the
76 // `std::enable_if` solution.
77 // Moreover this is almost guaranteed to be optimized away by a decent
78 // compiler.
79 return eval(e, is_team<T>());
80}
81
82template<class T, bool S>
83value_t basic_reg_lambda_f<T, S>::eval(const dataframe::example &e,
84 std::false_type) const
85{
86 return this->run(e.input);
87}
88
89template<class T, bool S>
90value_t basic_reg_lambda_f<T, S>::eval(const dataframe::example &e,
91 std::true_type) const
92{
93 D_DOUBLE avg(0), count(0);
94
95 // Calculate the running average.
96 for (const auto &core : this->team_)
97 {
98 const auto res(core.run(e.input));
99
100 if (has_value(res))
101 avg += (lexical_cast<D_DOUBLE>(res) - avg) / ++count;
102 }
103
104 if (count > 0.0)
105 return avg;
106
107 return {};
108}
109
110///
111/// \return a *failed* status
112///
113/// \warning This function is useful only for classification tasks.
114///
115template<class T, bool S>
116classification_result basic_reg_lambda_f<T, S>::tag(
117 const dataframe::example &) const
118{
119 return {0, 0};
120}
121
122///
123/// \param[in] a value produced by basic_lambda_f::operator()
124/// \return the string version of `a`
125///
126template<class T, bool S>
127std::string basic_reg_lambda_f<T, S>::name(const value_t &a) const
128{
129 return lexical_cast<std::string>(a);
130}
131
132///
133/// Calls (dynamic dispatch) polymhorphic model_metric `m` on `this`.
134///
135/// \param[in] m a metric we are evaluating
136/// \param[in] d a dataset
137/// \return the value of `this` according to metric `m`
138///
139template<class T, bool S>
140double basic_reg_lambda_f<T, S>::measure(const model_metric &m,
141 const dataframe &d) const
142{
143 return m(this, d);
144}
145
146///
147/// \return `true` if the object passes the internal consistency check
148///
149template<class T, bool S>
150bool basic_reg_lambda_f<T, S>::is_valid() const
151{
152 return detail::reg_lambda_f_storage<T, S>::is_valid();
153}
154
155///
156/// Saves the object on persistent storage.
157///
158/// \param[out] out output stream
159/// \return `true` if lambda was saved correctly
160///
161template<class T, bool S>
162bool basic_reg_lambda_f<T, S>::save(std::ostream &out) const
163{
164 return detail::reg_lambda_f_storage<T, S>::save(out);
165}
166
167///
168/// \param[in] d the training set
169///
170template<bool N>
171basic_class_lambda_f<N>::basic_class_lambda_f(const dataframe &d)
172 : detail::class_names<N>(d)
173{
174}
175
176///
177/// \param[in] e example to be classified
178/// \return the label of the class that includes `e`
179///
180template<bool N>
181value_t basic_class_lambda_f<N>::operator()(const dataframe::example &e) const
182{
183 return static_cast<D_INT>(this->tag(e).label);
184}
185
186///
187/// Calls (dynamic dispatch) polymhorphic model_metric `m` on `this`.
188///
189/// \param[in] m a metric we are evaluating
190/// \param[in] d a dataset
191/// \return the value of `this` according to metric `m`
192///
193template<bool N>
194double basic_class_lambda_f< N>::measure(const model_metric &m,
195 const dataframe &d) const
196{
197 return m(this, d);
198}
199
200///
201/// \param[in] a id of a class
202/// \return the name of class `a`
203///
204template<bool N>
205std::string basic_class_lambda_f<N>::name(const value_t &a) const
206{
207 return detail::class_names<N>::string(a);
208}
209
210///
211/// \param[in] ind individual "to be transformed" into a lambda function
212/// \param[in] d the training set
213/// \param[in] x_slot number of slots for each class of the training set
214///
215template<class T, bool S, bool N>
216basic_dyn_slot_lambda_f<T, S, N>::basic_dyn_slot_lambda_f(const T &ind,
217 dataframe &d,
218 unsigned x_slot)
219 : basic_class_lambda_f<N>(d), lambda_(ind),
220 slot_matrix_(d.classes() * x_slot, d.classes()),
221 slot_class_(d.classes() * x_slot), dataset_size_(0)
222{
223 Expects(d.classes() > 1);
224 Expects(x_slot);
225
226 fill_matrix(d, x_slot);
227
228 Ensures(is_valid());
229}
230
231///
232/// Constructs the object reading data from an input stream.
233///
234/// \param[in] in input stream
235/// \param[in] ss active symbol set
236///
237template<class T, bool S, bool N>
238basic_dyn_slot_lambda_f<T, S, N>::basic_dyn_slot_lambda_f(std::istream &in,
239 const symbol_set &ss)
240 : basic_class_lambda_f<N>(), lambda_(in, ss), slot_matrix_(), slot_class_(),
241 dataset_size_()
242{
243 static_assert(
244 S, "dyn_slot_lambda_f requires storage space for de-serialization");
245
246 if (!slot_matrix_.load(in))
247 throw exception::data_format(
248 "Cannot read dyn_slot_lambda_f matrix component");
249
250 std::generate_n(std::back_inserter(slot_class_), slot_matrix_.rows(),
251 [&in]
252 {
253 std::size_t v;
254 if (!(in >> v))
255 throw exception::data_format(
256 "Cannot read dyn_slot_lambda_f slot_class component");
257 return v;
258 });
259
260 if (!(in >> dataset_size_))
261 throw exception::data_format(
262 "Cannot read dyn_slot_lambda_f dataset_size component");
263
264 if (!detail::class_names<N>::load(in))
265 throw exception::data_format(
266 "Cannot read dyn_slot_lambda_f class_names component");
267
268 Ensures(is_valid());
269}
270
271///
272/// Sets up the data structures needed by the 'dynamic slot' algorithm.
273///
274/// \param[in] d the training set
275/// \param[in] x_slot number of slots for each class of the training set
276///
277template<class T, bool S, bool N>
278void basic_dyn_slot_lambda_f<T, S, N>::fill_matrix(dataframe &d,
279 unsigned x_slot)
280{
281 Expects(d.classes() > 1);
282 Expects(x_slot);
283
284 const auto n_slots(d.classes() * x_slot);
285 assert(n_slots == slot_matrix_.rows());
286 assert(slot_matrix_.cols() == d.classes());
287
288 // Here starts the slot-filling task.
289 slot_matrix_.fill(0);
290
291 // In the first step this method evaluates the program to obtain an output
292 // value for each training example. Based on the program output a
293 // bi-dimensional matrix is built (slot_matrix_(slot, class)).
294 for (const auto &example : d)
295 {
296 ++dataset_size_;
297
298 ++slot_matrix_(slot(example), label(example));
299 }
300
301 const auto unknown(d.classes());
302
303 // In the second step the method dynamically determine which class each
304 // slot belongs to by simply taking the class with the largest value at the
305 // slot...
306 for (auto i(decltype(n_slots){0}); i < n_slots; ++i)
307 {
308 const auto cols(slot_matrix_.cols());
309 auto best_class(decltype(cols){0}); // Initially assuming class 0 as best
310
311 // ...then looking for a better class among the remaining ones.
312 for (auto j(decltype(cols){1}); j < cols; ++j)
313 if (slot_matrix_(i, j) >= slot_matrix_(i, best_class))
314 best_class = j;
315
316 slot_class_[i] = slot_matrix_(i, best_class) ? best_class : unknown;
317 }
318
319 // Unknown slots can be a problem with new examples (not contained in the
320 // training set). We arbitrary assign them to the class of a neighbour
321 // slot (if available).
322 // Another interesting strategy would be to assign unknown slots to the
323 // largest class.
324 for (auto i(decltype(n_slots){0}); i < n_slots; ++i)
325 if (slot_class_[i] == unknown)
326 {
327 if (i && slot_class_[i - 1] != unknown)
328 slot_class_[i] = slot_class_[i - 1];
329 else if (i + 1 < n_slots && slot_class_[i + 1] != unknown)
330 slot_class_[i] = slot_class_[i + 1];
331 else
332 slot_class_[i] = 0;
333 }
334}
335
336///
337/// \param[in] e input data
338/// \return the slot example `e` falls into
339///
340template<class T, bool S, bool N>
341std::size_t basic_dyn_slot_lambda_f<T,S,N>::slot(
342 const dataframe::example &e) const
343{
344 const auto res(lambda_(e));
345
346 const auto ns(slot_matrix_.rows());
347 const auto last_slot(ns - 1);
348 if (!has_value(res))
349 return last_slot;
350
351 const auto val(lexical_cast<D_DOUBLE>(res));
352 const auto where(discretization(val, last_slot));
353
354 return (where >= ns) ? last_slot : where;
355}
356
357///
358/// \return the accuracy of the lambda function on the training set
359///
360template<class T, bool S, bool N>
361double basic_dyn_slot_lambda_f<T, S, N>::training_accuracy() const
362{
363 double ok(0.0);
364
365 const auto slots(slot_matrix_.rows());
366
367 for (auto i(decltype(slots){0}); i < slots; ++i)
368 ok += slot_matrix_(i, slot_class_[i]);
369
370 assert(dataset_size_ >= ok);
371
372 return ok / dataset_size_;
373}
374
375///
376/// \param[in] instance data to be classified
377/// \return the class of `instance` (numerical id) and the
378/// confidence level (in the range `[0,1]`)
379///
380template<class T, bool S, bool N>
381classification_result basic_dyn_slot_lambda_f<T, S, N>::tag(
382 const dataframe::example &instance) const
383{
384 const auto s(slot(instance));
385 const auto classes(slot_matrix_.cols());
386
387 unsigned total(0);
388 for (auto j(decltype(classes){0}); j < classes; ++j)
389 total += slot_matrix_(s, j);
390
391 const auto ok(slot_matrix_(s, slot_class_[s]));
392
393 const double confidence(!total ? 0.5 : static_cast<double>(ok) / total);
394
395 return {slot_class_[s], confidence};
396}
397
398///
399/// Saves the lambda on persistent storage.
400///
401/// \param[out] out output stream
402/// \return `true` on success
403///
404template<class T, bool S, bool N>
405bool basic_dyn_slot_lambda_f<T, S, N>::save(std::ostream &out) const
406{
407 if (!lambda_.save(out))
408 return false;
409
410 if (!slot_matrix_.save(out))
411 return false;
412
413 // Don't need to save slot_class_.size() since it's equal to
414 // slot_matrix_.rows()
415 for (auto s : slot_class_)
416 if (!(out << s << '\n'))
417 return false;
418
419 if (!(out << dataset_size_ << '\n'))
420 return false;
421
422 return detail::class_names<N>::save(out);
423}
424
425///
426/// \return `true` if the object passes the internal consistency check
427///
428template<class T, bool S, bool N>
429bool basic_dyn_slot_lambda_f<T, S, N>::is_valid() const
430{
431 if (slot_matrix_.cols() <= 1) // too few classes
432 return false;
433
434 if (slot_matrix_.rows() != slot_class_.size())
435 return false;
436
437 return true;
438}
439
440///
441/// \param[in] ind individual "to be transformed" into a lambda function
442/// \param[in] d the training set
443///
444template<class T, bool S, bool N>
445basic_gaussian_lambda_f<T, S, N>::basic_gaussian_lambda_f(const T &ind,
446 dataframe &d)
447 : basic_class_lambda_f<N>(d), lambda_(ind), gauss_dist_(d.classes())
448{
449 Expects(d.classes() > 1);
450
451 fill_vector(d);
452
453 Ensures(is_valid());
454}
455
456///
457/// Constructs the object reading data from an input stream.
458///
459/// \param[in] in input stream
460/// \param[in] ss active symbol set
461///
462template<class T, bool S, bool N>
463basic_gaussian_lambda_f<T, S, N>::basic_gaussian_lambda_f(std::istream &in,
464 const symbol_set &ss)
465 : basic_class_lambda_f<N>(), lambda_(in, ss), gauss_dist_()
466{
467 static_assert(
468 S, "gaussian_lambda_f requires storage space for de-serialization");
469
470 typename decltype(gauss_dist_)::size_type n;
471 if (!(in >> n))
472 throw exception::data_format(
473 "Cannot read gaussian_lambda_f size component");
474
475 for (decltype(n) i(0); i < n; ++i)
476 {
477 distribution<number> d;
478 if (!d.load(in))
479 throw exception::data_format(
480 "Cannot read gaussian_lambda_f distribution component");
481
482 gauss_dist_.push_back(d);
483 }
484
485 if (!detail::class_names<N>::load(in))
486 throw exception::data_format(
487 "Cannot read gaussian_lambda_f class_names component");
488
489 Ensures(is_valid());
490}
491
492///
493/// Sets up the data structures needed by the gaussian algorithm.
494///
495/// \param[in] d the training set
496///
497template<class T, bool S, bool N>
498void basic_gaussian_lambda_f<T, S, N>::fill_vector(dataframe &d)
499{
500 Expects(d.classes() > 1);
501
502 // For a set of training data, we assume that the behaviour of a program
503 // classifier is modelled using multiple Gaussian distributions, each of
504 // which corresponds to a particular class. The distribution of a class is
505 // determined by evaluating the program on the examples of the class in
506 // the training set. This is done by taking the mean and standard deviation
507 // of the program outputs for those training examples for that class.
508 for (const auto &example : d)
509 {
510 const auto res(lambda_(example));
511
512 number val(has_value(res) ? lexical_cast<D_DOUBLE>(res) : 0.0);
513 const number cut(10000000.0);
514 if (val > cut)
515 val = cut;
516 else if (val < -cut)
517 val = -cut;
518
519 gauss_dist_[label(example)].add(val);
520 }
521}
522
523///
524/// \param[in] example input value whose class we are interested in
525/// \return the class of `example` (numerical id) and the confidence
526/// level (how sure you can be that `example` is properly
527/// classified. The value is in the `[0,1]` interval and the
528/// sum of all the confidence levels of each class equals
529/// `1`)
530///
531template<class T, bool S, bool N>
532classification_result basic_gaussian_lambda_f<T, S, N>::tag(
533 const dataframe::example &example) const
534{
535 const auto res(lambda_(example));
536 const number x(has_value(res) ? lexical_cast<D_DOUBLE>(res) : 0.0);
537
538 number val_(0.0), sum_(0.0);
539 class_t probable_class(0);
540
541 const auto classes(static_cast<unsigned>(gauss_dist_.size()));
542 for (auto i(decltype(classes){0}); i < classes; ++i)
543 {
544 const number distance(std::fabs(x - gauss_dist_[i].mean()));
545 const number variance(gauss_dist_[i].variance());
546
547 number p(0.0);
548 if (issmall(variance)) // These are borderline cases
549 if (issmall(distance)) // These are borderline cases
550 p = 1.0;
551 else
552 p = 0.0;
553 else // This is the standard case
554 p = std::exp(-distance * distance / variance);
555
556 if (p > val_)
557 {
558 val_ = p;
559 probable_class = i;
560 }
561
562 sum_ += p;
563 }
564
565 // Normalized confidence value.
566 // Do not change sum_ > 0.0 with
567 // - issmall(sum_) => when sum_ is small, val_ is smaller and the division
568 // works well.
569 // - sum_ => it's the same thing but will produce a warning with
570 // -Wfloat-equal
571 const double confidence(sum_ > 0.0 ? val_ / sum_ : 0.0);
572
573 return {probable_class, confidence};
574}
575
576///
577/// Saves the lambda on persistent storage.
578///
579/// \param[out] out output stream
580/// \return `true` on success
581///
582template<class T, bool S, bool N>
583bool basic_gaussian_lambda_f<T, S, N>::save(std::ostream &out) const
584{
585 if (!lambda_.save(out))
586 return false;
587
588 if (!(out << gauss_dist_.size() << '\n'))
589 return false;
590
591 for (const auto &g : gauss_dist_)
592 if (!g.save(out))
593 return false;
594
595 return detail::class_names<N>::save(out);
596}
597
598///
599/// \return `true` if the object passes the internal consistency check
600///
601template<class T, bool S, bool N>
602bool basic_gaussian_lambda_f<T, S, N>::is_valid() const
603{
604 return true;
605}
606
607///
608/// Constructs the object reading data from an input stream.
609///
610/// \param[in] ind individual "to be transformed" into a lambda function
611/// \param[in] d the training set
612///
613template<class T, bool S, bool N>
614basic_binary_lambda_f<T, S, N>::basic_binary_lambda_f(const T &ind,
615 dataframe &d)
616 : basic_class_lambda_f<N>(d), lambda_(ind)
617{
618 Expects(d.classes() == 2);
619
620 Ensures(is_valid());
621}
622
623///
624/// \param[in] in input stream
625/// \param[in] ss active symbol set
626///
627template<class T, bool S, bool N>
628basic_binary_lambda_f<T, S, N>::basic_binary_lambda_f(std::istream &in,
629 const symbol_set &ss)
630 : basic_class_lambda_f<N>(), lambda_(in, ss)
631{
632 static_assert(
633 S, "binary_lambda_f requires storage space for de-serialization");
634
635 if (!detail::class_names<N>::load(in))
636 throw exception::data_format(
637 "Cannot read binary_lambda_f class_names component");
638
639 Ensures(is_valid());
640}
641
642///
643/// \param[in] e input example for the lambda function
644/// \return the class of `e` (numerical id) and the confidence level (in
645/// the `[0,1]` interval)
646///
647template<class T, bool S, bool N>
648classification_result basic_binary_lambda_f<T, S, N>::tag(
649 const dataframe::example &e) const
650{
651 const auto res(lambda_(e));
652 const number val(has_value(res) ? lexical_cast<D_DOUBLE>(res) : 0.0);
653
654 return {val > 0.0 ? 1u : 0u, std::fabs(val)};
655}
656
657///
658/// \return `true` if the object passes the internal consistency check
659///
660template<class T, bool S, bool N>
661bool basic_binary_lambda_f<T, S, N>::is_valid() const
662{
663 return true;
664}
665
666///
667/// Saves the lambda on persistent storage.
668///
669/// \param[out] out output stream
670/// \return `true` on success
671///
672template<class T, bool S, bool N>
673bool basic_binary_lambda_f<T, S, N>::save(std::ostream &out) const
674{
675 if (!lambda_.save(out))
676 return false;
677
678 return detail::class_names<N>::save(out);
679}
680
681///
682/// \param[in] t team "to be transformed" into a lambda function
683/// \param[in] d the training set
684/// \param[in] args auxiliary parameters for the specific lambda function
685///
686template<class T, bool S, bool N, template<class, bool, bool> class L,
687 team_composition C>
688template<class... Args>
689team_class_lambda_f<T, S, N, L, C>::team_class_lambda_f(const team<T> &t,
690 dataframe &d,
691 Args&&... args)
692 : basic_class_lambda_f<N>(d), classes_(d.classes())
693{
694 team_.reserve(t.individuals());
695 for (const auto &ind : t)
696 team_.emplace_back(ind, d, std::forward<Args>(args)...);
697}
698
699///
700/// Constructs the object reading data from an input stream.
701///
702/// \param[in] in input stream
703/// \param[in] ss active symbol set
704///
705template<class T, bool S, bool N, template<class, bool, bool> class L,
706 team_composition C>
707team_class_lambda_f<T, S, N, L, C>::team_class_lambda_f(std::istream &in,
708 const symbol_set &ss)
709 : basic_class_lambda_f<N>(), classes_()
710{
711 static_assert(
712 S, "team_class_lambda_f requires storage space for de-serialization");
713
714 if (!(in >> classes_))
715 throw exception::data_format("Cannot read number of classes");
716
717 typename decltype(team_)::size_type s;
718 if (!(in >> s))
719 throw exception::data_format("Cannot read team size");
720
721 team_.reserve(s);
722 for (unsigned i(0); i < s; ++i)
723 team_.emplace_back(in, ss);
724
725 if (!detail::class_names<N>::load(in))
726 throw exception::data_format("Cannot read class_names");
727}
728
729///
730/// Specialized method for teams.
731///
732/// \param[in] instance data to be classified
733/// \return the class of `instance` (numerical id) and the
734/// confidence level (in the `0,1]` interval)
735///
736/// * `team_composition::mv` the class which most of the individuals predict
737/// for a given example is selected as team output.
738/// * `team_composition::wta` the winner is the individual with the highest
739/// confidence in its decision. Specialization may emerge if different
740/// members of the team win this contest for different fitness cases (of
741/// curse, it is not a feasible alternative to select the member with the
742/// best fitness. Then a decision on unknown data is only possible if the
743/// right outputs are known in advance and is not made by the team itself).
744///
745template<class T, bool S, bool N, template<class, bool, bool> class L,
746 team_composition C>
747classification_result team_class_lambda_f<T, S, N, L, C>::tag(
748 const dataframe::example &instance) const
749{
750 if constexpr (C == team_composition::wta)
751 {
752 const auto size(team_.size());
753 auto best(team_[0].tag(instance));
754
755 for (auto i(decltype(size){1}); i < size; ++i)
756 {
757 const auto res(team_[i].tag(instance));
758
759 if (res.sureness > best.sureness)
760 best = res;
761 }
762
763 return best;
764 }
765 else if constexpr (C == team_composition::mv)
766 {
767 std::vector<unsigned> votes(classes_);
768
769 for (const auto &lambda : team_)
770 ++votes[lambda.tag(instance).label];
771
772 class_t max(0);
773 for (auto i(max + 1); i < classes_; ++i)
774 if (votes[i] > votes[max])
775 max = i;
776
777 return {max, static_cast<double>(votes[max]) /
778 static_cast<double>(team_.size())};
779 }
780}
781
782///
783/// Saves the lambda team on persistent storage.
784///
785/// \param[out] out output stream
786/// \return `true` on success
787///
788template<class T, bool S, bool N, template<class, bool, bool> class L,
789 team_composition C>
790bool team_class_lambda_f<T, S, N, L, C>::save(std::ostream &out) const
791{
792 if (!(out << classes_ << '\n'))
793 return false;
794
795 if (!(out << team_.size() << '\n'))
796 return false;
797
798 for (const auto &i : team_)
799 if (!i.save(out))
800 return false;
801
802 return detail::class_names<N>::save(out);
803}
804
805///
806/// \return Class ID used for serialization.
807///
808template<class T, bool S, bool N, template<class, bool, bool> class L,
809 team_composition C>
810std::string team_class_lambda_f<T, S, N, L, C>::serialize_id() const
811{
812 Expects(team_.size());
813 return "TEAM_" + L<T, S, N>::SERIALIZE_ID;
814}
815
816///
817/// \return `true` if the object passes the internal consistency check
818///
819template<class T, bool S, bool N, template<class, bool, bool> class L,
820 team_composition C>
821bool team_class_lambda_f<T, S, N, L, C>::is_valid() const
822{
823 return classes_ > 1;
824}
825
826namespace serialize
827{
828
829namespace lambda
830{
831
832namespace detail
833{
834using build_func = std::unique_ptr<basic_src_lambda_f> (*)(std::istream &,
835 const symbol_set &);
836
837template<class U>
838std::unique_ptr<basic_src_lambda_f> build(std::istream &in,
839 const symbol_set &ss)
840{
841 return std::make_unique<U>(in, ss);
842}
843
844extern std::map<std::string, build_func> factory_;
845}
846
847///
848/// Allows insertion of user defined classificators.
849///
850template<class U>
851bool insert(const std::string &id)
852{
853 Expects(!id.empty());
854 return detail::factory_.insert({id, detail::build<U>}).second;
855}
856
857template<class T>
858std::unique_ptr<basic_src_lambda_f> load(std::istream &in,
859 const symbol_set &ss)
860{
861 if (detail::factory_.find(reg_lambda_f<T>::SERIALIZE_ID)
862 == detail::factory_.end())
863 {
864 insert<reg_lambda_f<T>>(reg_lambda_f<T>::SERIALIZE_ID);
865 insert<dyn_slot_lambda_f<T>>(dyn_slot_lambda_f<T>::SERIALIZE_ID);
866 insert<gaussian_lambda_f<T>>(gaussian_lambda_f<T>::SERIALIZE_ID);
867 insert<binary_lambda_f<T>>(binary_lambda_f<T>::SERIALIZE_ID);
868 }
869
870 std::string id;
871 if (!(in >> id))
872 return nullptr;
873
874 const auto iter(detail::factory_.find(id));
875 if (iter != detail::factory_.end())
876 return iter->second(in, ss);
877
878 return nullptr;
879}
880
881} // namespace lambda
882
883} // namespace serialize
884
885#endif // include guard