Ultra 0.9.4
Loading...
Searching...
No Matches
real.h
Go to the documentation of this file.
1
12
13#if !defined(ULTRA_REAL_PRIMITIVE_H)
14#define ULTRA_REAL_PRIMITIVE_H
15
16#include "kernel/nullary.h"
17#include "kernel/random.h"
18#include "kernel/gp/function.h"
19#include "kernel/terminal.h"
20#include "utility/misc.h"
21
22#include <cmath>
23#include <limits>
24#include <string>
25
26namespace ultra::real
27{
28
35[[nodiscard]] inline D_DOUBLE base(const value_t &v)
36{
37 return std::get<D_DOUBLE>(v);
38}
39
40// We assume that errors during floating-point operations aren't terminal
41// errors. So we don't try to prevent domain errors (e.g. square root of a
42// negative number) or range error (e.g. `pow(10.0, 1e6)`) checking arguments
43// beforehand (domain errors could be prevented by carefully bounds checking
44// the arguments before calling functions and taking alternative action if
45// the bounds are violated; range errors usually cannot be prevented, as they
46// are dependent on the implementation of floating-point numbers, as well as
47// the function being applied).
48// Instead we detect them and take alternative action (usually returning an
49// empty value).
50static_assert(std::numeric_limits<D_DOUBLE>::is_iec559,
51 "Ultra requires IEC 559/IEEE 754 floating-point types");
52
56class number : public terminal
57{
58public:
59 explicit number(D_DOUBLE m = -1000.0, D_DOUBLE s = 1000.0,
61 : terminal("REAL", c), min_(m), sup_(s)
62 {
63 Expects(m < s);
64 }
65
66 [[nodiscard]] D_DOUBLE min() const noexcept { return min_; }
67 [[nodiscard]] D_DOUBLE sup() const noexcept { return sup_; }
68
69 [[nodiscard]] value_t instance() const final
70 { return random::between(min_, sup_); }
71
72private:
73 const D_DOUBLE min_, sup_;
74};
75
79class integer : public terminal
80{
81public:
82 explicit integer(D_INT m = -128, D_INT s = 128,
84 : terminal("IREAL", c), min_(m), sup_(s)
85 {
86 Expects(m < s);
87 }
88
89 [[nodiscard]] D_DOUBLE min() const noexcept
90 {return static_cast<D_DOUBLE>(min_);}
91 [[nodiscard]] D_DOUBLE sup() const noexcept
92 {return static_cast<D_DOUBLE>(sup_);}
93
94 [[nodiscard]] value_t instance() const final
95 { return static_cast<D_DOUBLE>(random::between(min_, sup_)); }
96
97private:
98 const D_INT min_, sup_;
99};
100
101class literal : public terminal
102{
103public:
104 explicit literal(D_DOUBLE v, category_t c = symbol::default_category)
105 : terminal(std::to_string(v), c), val_(v)
106 {
107 }
108
109 [[nodiscard]] value_t instance() const noexcept final { return val_; }
110
111private:
112 const D_DOUBLE val_;
113};
114
118class abs : public function
119{
120public:
121 explicit abs(category_t c = symbol::default_category)
122 : function("FABS", c, {c}) {}
123
124 [[nodiscard]] std::string to_string(format f) const final
125 {
126 switch (f)
127 {
128 case cpp_format: return "std::abs({0})";
129 case python_format: return "abs({0})";
130 default: return "fabs({0})";
131 }
132 }
133
134 [[nodiscard]] value_t eval(const params &pars) const final
135 {
136 const auto p(pars[0]);
137 if (!has_value(p)) return p;
138
139 assert(std::isfinite(base(p)));
140 return std::fabs(base(p));
141 }
142};
143
147class add : public function
148{
149public:
150 explicit add(category_t c = symbol::default_category)
151 : function("FADD", c, {c, c}) {}
152
153 [[nodiscard]] std::string to_string(format) const final
154 {
155 return "({0}+{1})";
156 }
157
158 [[nodiscard]] value_t eval(const params &pars) const final
159 {
160 const auto p0(pars[0]);
161 if (!has_value(p0)) return p0;
162
163 const auto p1(pars[1]);
164 if (!has_value(p1)) return p1;
165
166 const auto ret(base(p0) + base(p1));
167 if (!std::isfinite(ret)) return {};
168
169 return ret;
170 }
171};
172
186class aq : public function
187{
188public:
192 : function("AQ", r, pt)
193 {
194 Expects(pt.size() == 2);
195 }
196
197 [[nodiscard]] std::string to_string(format f) const final
198 {
199 switch (f)
200 {
201 case cpp_format: return "({0}/std::sqrt(1.0+std::pow({1},2.0)))";
202 case python_format: return "({0}/math.sqrt(1.0+math.pow({1},2.0)))";
203 default: return "({0}/sqrt(1.0+pow({1},2.0)))";
204 }
205 }
206
207 [[nodiscard]] value_t eval(const params &pars) const final
208 {
209 const auto p0(pars[0]);
210 if (!has_value(p0)) return p0;
211
212 const auto p1(pars[1]);
213 if (!has_value(p1)) return p1;
214
215 const auto x(base(p0)), y(base(p1));
216 const auto ret(x / std::sqrt(1.0 + y * y));
217 if (!std::isfinite(ret)) return {};
218
219 return ret;
220 }
221};
222
226class cos : public function
227{
228public:
229 explicit cos(category_t c = symbol::default_category)
230 : function("FCOS", c, {c}) {}
231
232 [[nodiscard]] std::string to_string(format f) const final
233 {
234 switch (f)
235 {
236 case cpp_format: return "std::cos({0})";
237 case python_format: return "math.cos({0})";
238 default: return "cos({0})";
239 }
240 }
241
242 [[nodiscard]] value_t eval(const params &pars) const final
243 {
244 const auto p(pars[0]);
245 if (!has_value(p)) return p;
246
247 assert(!std::isinf(base(p)));
248 return std::cos(base(p));
249 }
250};
251
255class div : public function
256{
257public:
258 explicit div(return_type r = symbol::default_category,
261 : function("FDIV", r, pt)
262 {
263 Expects(pt.size() == 2);
264 }
265
266 [[nodiscard]] std::string to_string(format) const final
267 {
268 return "({0}/{1})";
269 }
270
271 [[nodiscard]] value_t eval(const params &pars) const final
272 {
273 const auto p0(pars[0]);
274 if (!has_value(p0)) return p0;
275
276 const auto p1(pars[1]);
277 if (!has_value(p1)) return p1;
278
279 const auto ret(base(p0) / base(p1));
280 if (!std::isfinite(ret)) return {};
281
282 return ret;
283 }
284};
285
289class gt : public function
290{
291public:
295 : function(">", r, pt)
296 {
297 Expects(pt.size() == 2);
298 Expects(pt[0] == pt[1]);
299 }
300
301 [[nodiscard]] std::string to_string(format f) const final
302 {
303 switch (f)
304 {
305 case cpp_format: return "std::isgreater({0},{1})";
306 default: return "({0}>{1})";
307 }
308 }
309
310 [[nodiscard]] value_t eval(const params &pars) const final
311 {
312 const auto p0(pars[0]);
313 if (!has_value(p0)) return p0;
314
315 const auto p1(pars[1]);
316 if (!has_value(p1)) return p1;
317
318 return std::isgreater(base(p0), base(p1));
319 // If one or both arguments of `isgreater` are `NaN`, the function returns
320 // `false`, but no FE_INVALID exception is raised (note that the
321 // expression `v0 > v1` may raise an exception in this case).
322 }
323};
324
328class idiv : public function
329{
330public:
331 explicit idiv(return_type r = symbol::default_category,
334 : function("FIDIV", r, pt)
335 {
336 Expects(pt.size() == 2);
337 Expects(pt[0] == pt[1]);
338 }
339
340 [[nodiscard]] std::string to_string(format f) const final
341 {
342 switch (f)
343 {
344 case cpp_format: return "std::floor({0}/{1})";
345 case python_format: return "({0}//{1})";
346 default: return "floor({0}/{1})";
347 }
348 }
349
350 [[nodiscard]] value_t eval(const params &pars) const final
351 {
352 const auto p0(pars[0]);
353 if (!has_value(p0)) return p0;
354
355 const auto p1(pars[1]);
356 if (!has_value(p1)) return p1;
357
358 const auto ret(std::floor(base(p0) / base(p1)));
359 if (!std::isfinite(ret)) return {};
360
361 return ret;
362 }
363};
364
368class ife : public function
369{
370public:
371 explicit ife(return_type r = symbol::default_category,
376 : function("FIFE", r, pt)
377 {
378 Expects(pt.size() == 4);
379 Expects(r == pt[2]);
380 Expects(pt[0] == pt[1]);
381 Expects(pt[2] == pt[3]);
382 }
383
384 [[nodiscard]] std::string to_string(format f) const final
385 {
386 switch (f)
387 {
388 case cpp_format:
389 return "(std::fabs({0}-{1})<2*std::numeric_limits<double>::epsilon() ? "
390 "{2} : {3})";
391 case python_format:
392 return "({2} if math.isclose({0}, {1}) else {3})";
393 default:
394 return "(fabs({0}-{1}) < 2*DBL_EPSILON ? {2} : {3})";
395 }
396 }
397
398 [[nodiscard]] value_t eval(const params &pars) const final
399 {
400 const auto p0(pars[0]);
401 if (!has_value(p0)) return p0;
402
403 const auto p1(pars[1]);
404 if (!has_value(p1)) return p1;
405
406 if (issmall(base(p0) - base(p1)))
407 return pars[2];
408 else
409 return pars[3];
410 }
411};
412
416class ifl : public function
417{
418public:
419 explicit ifl(return_type r = symbol::default_category,
424 : function("FIFL", r, pt)
425 {
426 Expects(pt.size() == 4);
427 Expects(r == pt[2]);
428 Expects(pt[0] == pt[1]);
429 Expects(pt[2] == pt[3]);
430 }
431
432 [[nodiscard]] std::string to_string(format f) const final
433 {
434 switch (f)
435 {
436 case python_format: return "({2} if {0}<{1} else {3})";
437 default: return "({0}<{1} ? {2} : {3})";
438 }
439 }
440
441 [[nodiscard]] value_t eval(const params &pars) const final
442 {
443 const auto p0(pars[0]);
444 if (!has_value(p0)) return p0;
445
446 const auto p1(pars[1]);
447 if (!has_value(p1)) return p1;
448
449 const auto v0(base(p0)), v1(base(p1));
450 if (std::isless(v0, v1))
451 return pars[2];
452 else
453 return pars[3];
454 }
455
456};
457
461class ifz : public function
462{
463public:
464 explicit ifz(return_type r = symbol::default_category,
468 : function("FIFZ", r, pt)
469 {
470 Expects(pt.size() == 3);
471 Expects(r == pt[1]);
472 Expects(pt[1] == pt[2]);
473 }
474
475 [[nodiscard]] std::string to_string(format f) const final
476 {
477 switch (f)
478 {
479 case cpp_format:
480 return "(std::fabs({0})<2*std::numeric_limits<double>::epsilon() ? "
481 "{1} : {2})";
482 case python_format:
483 return "({1} if abs({0}) < 1e-10 else {2})";
484 default:
485 return "(fabs({0})<2*DBL_EPSILON ? {1} : {2})";
486 }
487 }
488
489 [[nodiscard]] value_t eval(const params &pars) const final
490 {
491 const auto p0(pars[0]);
492 if (!has_value(p0)) return p0;
493
494 return issmall(base(p0)) ? pars[1] : pars[2];
495 }
496};
497
501class length : public function
502{
503public:
504 explicit length(return_type r, const param_data_types &pt)
505 : function("FLENGTH", r, pt)
506 {
507 Expects(pt.size() == 1);
508 Expects(r != pt[0]);
509 }
510
511 [[nodiscard]] std::string to_string(format f) const final
512 {
513 switch (f)
514 {
515 case cpp_format: return "std::string({0}).length()";
516 case python_format: return "len({0})";
517 default: return "strlen({0})";
518 }
519 }
520
521 [[nodiscard]] value_t eval(const params &pars) const final
522 {
523 const auto p(pars[0]);
524 if (!has_value(p)) return p;
525
526 return static_cast<D_DOUBLE>(std::get<D_STRING>(p).length());
527 }
528};
529
538class ln : public function
539{
540public:
541 explicit ln(category_t c = symbol::default_category)
542 : function("FLN", c, {c}) {}
543
544 [[nodiscard]] std::string to_string(format f) const final
545 {
546 switch (f)
547 {
548 case cpp_format: return "std::log({0})";
549 case python_format: return "math.log({0})";
550 default: return "log({0})";
551 }
552 }
553
559 [[nodiscard]] value_t eval(const params &pars) const final
560 {
561 const auto p0(pars[0]);
562 if (!has_value(p0)) return p0;
563
564 const auto ret(std::log(base(p0)));
565 if (!std::isfinite(ret)) return {};
566
567 return ret;
568 }
569};
570
579class exp : public function
580{
581public:
582 explicit exp(category_t c = symbol::default_category)
583 : function("FEXP", c, {c}) {}
584
585 [[nodiscard]] std::string to_string(format f) const final
586 {
587 switch (f)
588 {
589 case cpp_format: return "std::exp({0})";
590 case python_format: return "math.exp({0})";
591 default: return "exp({0})";
592 }
593 }
594
600 [[nodiscard]] value_t eval(const params &pars) const final
601 {
602 const auto p0(pars[0]);
603 if (!has_value(p0)) return p0;
604
605 const auto ret(std::exp(base(p0)));
606 if (!std::isfinite(ret)) return {};
607
608 return ret;
609 }
610};
611
615class lt : public function
616{
617public:
621 : function("<", r, pt)
622 {
623 Expects(pt.size() == 2);
624 Expects(pt[0] == pt[1]);
625 }
626
627 [[nodiscard]] std::string to_string(format f) const final
628 {
629 switch (f)
630 {
631 case cpp_format: return "std::isless({0},{1})";
632 default: return "({0}<{1})";
633 }
634 }
635
636 [[nodiscard]] value_t eval(const params &pars) const final
637 {
638 const auto p0(pars[0]);
639 if (!has_value(p0)) return p0;
640
641 const auto p1(pars[1]);
642 if (!has_value(p1)) return p1;
643
644 return std::isless(base(p0), base(p1));
645 // If one or both arguments of `isless` are NaN, the function returns
646 // false, but no FE_INVALID exception is raised (note that the
647 // expression `v0 < v1` may raise an exception in this case).
648 }
649};
650
654class max : public function
655{
656public:
657 explicit max(category_t c = symbol::default_category)
658 : function("FMAX", c, {c, c}) {}
659
660 [[nodiscard]] std::string to_string(format f) const final
661 {
662 switch (f)
663 {
664 case cpp_format: return "std::fmax({0},{1})";
665 case python_format: return "max({0},{1})";
666 default: return "fmax({0},{1})";
667 }
668 }
669
670 [[nodiscard]] value_t eval(const params &pars) const final
671 {
672 const auto p0(pars[0]);
673 if (!has_value(p0)) return p0;
674
675 const auto p1(pars[1]);
676 if (!has_value(p1)) return p1;
677
678 const auto ret(std::fmax(base(p0), base(p1)));
679 if (!std::isfinite(ret)) return {};
680
681 return ret;
682 }
683};
684
688class mod : public function
689{
690public:
691 explicit mod(return_type r = symbol::default_category,
694 : function("FMOD", r, pt)
695 {
696 Expects(pt.size() == 2);
697 Expects(pt[0] == pt[1]);
698 }
699
700 [[nodiscard]] std::string to_string(format f) const final
701 {
702 switch (f)
703 {
704 case cpp_format: return "std::fmod({0},{1})";
705 case python_format: return "({0} % {1})";
706 default: return "fmod({0},{1})";
707 }
708 }
709
710 [[nodiscard]] value_t eval(const params &pars) const final
711 {
712 const auto p0(pars[0]);
713 if (!has_value(p0)) return p0;
714
715 const auto p1(pars[1]);
716 if (!has_value(p1)) return p1;
717
718 const auto ret(std::fmod(base(p0), base(p1)));
719 if (!std::isfinite(ret)) return {};
720
721 return ret;
722 }
723};
724
728class mul : public function
729{
730public:
731 explicit mul(return_type r = symbol::default_category,
734 : function("FMUL", r, pt)
735 {
736 Expects(pt.size() == 2);
737 }
738
739 [[nodiscard]] std::string to_string(format) const final
740 {
741 return "({0}*{1})";
742 }
743
744 [[nodiscard]] value_t eval(const params &pars) const final
745 {
746 const auto p0(pars[0]);
747 if (!has_value(p0)) return p0;
748
749 const auto p1(pars[1]);
750 if (!has_value(p1)) return p1;
751
752 const auto ret(base(p0) * base(p1));
753 if (!std::isfinite(ret)) return {};
754
755 return ret;
756 }
757};
758
762class sin : public function
763{
764public:
765 explicit sin(category_t c = symbol::default_category)
766 : function("FSIN", c, {c}) {}
767
768 [[nodiscard]] std::string to_string(format f) const final
769 {
770 switch (f)
771 {
772 case cpp_format: return "std::sin({0})";
773 case python_format: return "math.sin({0})";
774 default: return "sin({0})";
775 }
776 }
777
778 [[nodiscard]] value_t eval(const params &pars) const final
779 {
780 const auto p(pars[0]);
781 if (!has_value(p)) return p;
782
783 assert(!std::isinf(base(p)));
784 return std::sin(base(p));
785 }
786};
787
791class sqrt : public function
792{
793public:
794 explicit sqrt(category_t c = symbol::default_category)
795 : function("FSQRT", c, {c}) {}
796
797 [[nodiscard]] std::string to_string(format f) const final
798 {
799 switch (f)
800 {
801 case cpp_format: return "std::sqrt({0})";
802 case python_format: return "math.sqrt({0})";
803 default: return "sqrt({0})";
804 }
805 }
806
807 [[nodiscard]] value_t eval(const params &pars) const final
808 {
809 const auto p(pars[0]);
810 if (!has_value(p)) return p;
811
812 const auto v(base(p));
813 if (std::isless(v, 0.0))
814 return {};
815
816 return std::sqrt(v);
817 }
818};
819
823class sub : public function
824{
825public:
826 explicit sub(category_t c = symbol::default_category)
827 : function("FSUB", c, {c, c}) {}
828
829 [[nodiscard]] std::string to_string(format) const final
830 {
831 return "({0}-{1})";
832 }
833
834 [[nodiscard]] value_t eval(const params &pars) const final
835 {
836 const auto p0(pars[0]);
837 if (!has_value(p0)) return p0;
838
839 const auto p1(pars[1]);
840 if (!has_value(p1)) return p1;
841
842 const auto ret(base(p0) - base(p1));
843 if (!std::isfinite(ret)) return {};
844
845 return ret;
846 }
847};
848
852class sigmoid : public function
853{
854public:
855 explicit sigmoid(category_t c = symbol::default_category)
856 : function("FSIGMOID", c, {c}) {}
857
858 [[nodiscard]] std::string to_string(format f) const final
859 {
860 switch (f)
861 {
862 case cpp_format: return "1.0 / (1.0 + std::exp(-{0}))";
863 case python_format: return "1. / (1. + math.exp(-{0}))";
864 default: return "1 / (1 + exp(-{0}))";
865 }
866 }
867
868 [[nodiscard]] value_t eval(const params &pars) const final
869 {
870 const auto p0(pars[0]);
871 if (!has_value(p0)) return p0;
872
873 // The sigmoid function can be expressed in one of two equivalent ways:
874 // sigmoid(x) = 1 / (1 + exp(-x)) = exp(x) / (exp(x) + 1)
875 // Each version can be used in order to avoid numerical overflow in extreme
876 // cases (`x --> +inf` and `x --> -inf` respectively).
877 const auto x(base(p0));
878
879 return x >= 0.0 ? 1.0 / (1.0 + std::exp(-x))
880 : std::exp(x) / (1.0 + std::exp(x));
881 }
882};
883
884} // namespace ultra::real
885
886#endif // include guard
Definition function.h:108
std::vector< category_t > param_data_types
Definition function.h:49
function(const std::string &, return_type, param_data_types)
Definition function.cc:27
category_t return_type
Type used to describe the return category of the function.
Definition function.h:52
value_t eval(const params &pars) const final
Definition real.h:134
std::string to_string(format f) const final
Definition real.h:124
std::string to_string(format) const final
Definition real.h:153
value_t eval(const params &pars) const final
Definition real.h:158
value_t eval(const params &pars) const final
Definition real.h:207
std::string to_string(format f) const final
Definition real.h:197
value_t eval(const params &pars) const final
Definition real.h:242
std::string to_string(format f) const final
Definition real.h:232
std::string to_string(format) const final
Definition real.h:266
value_t eval(const params &pars) const final
Definition real.h:271
std::string to_string(format f) const final
Definition real.h:585
value_t eval(const params &pars) const final
Definition real.h:600
std::string to_string(format f) const final
Definition real.h:301
value_t eval(const params &pars) const final
Definition real.h:310
value_t eval(const params &pars) const final
Definition real.h:350
std::string to_string(format f) const final
Definition real.h:340
value_t eval(const params &pars) const final
Definition real.h:398
std::string to_string(format f) const final
Definition real.h:384
value_t eval(const params &pars) const final
Definition real.h:441
std::string to_string(format f) const final
Definition real.h:432
value_t eval(const params &pars) const final
Definition real.h:489
std::string to_string(format f) const final
Definition real.h:475
value_t instance() const final
Definition real.h:94
std::string to_string(format f) const final
Definition real.h:511
value_t eval(const params &pars) const final
Definition real.h:521
value_t instance() const noexcept final
Definition real.h:109
value_t eval(const params &pars) const final
Definition real.h:559
std::string to_string(format f) const final
Definition real.h:544
std::string to_string(format f) const final
Definition real.h:627
value_t eval(const params &pars) const final
Definition real.h:636
std::string to_string(format f) const final
Definition real.h:660
value_t eval(const params &pars) const final
Definition real.h:670
std::string to_string(format f) const final
Definition real.h:700
value_t eval(const params &pars) const final
Definition real.h:710
std::string to_string(format) const final
Definition real.h:739
value_t eval(const params &pars) const final
Definition real.h:744
value_t instance() const final
Definition real.h:69
value_t eval(const params &pars) const final
Definition real.h:868
std::string to_string(format f) const final
Definition real.h:858
value_t eval(const params &pars) const final
Definition real.h:778
std::string to_string(format f) const final
Definition real.h:768
std::string to_string(format f) const final
Definition real.h:797
value_t eval(const params &pars) const final
Definition real.h:807
value_t eval(const params &pars) const final
Definition real.h:834
std::string to_string(format) const final
Definition real.h:829
format
Supported rendering formats for symbol stringification.
Definition symbol.h:72
unsigned category_t
Definition symbol.h:55
static constexpr category_t default_category
Default category assigned when typing is not used.
Definition symbol.h:58
Definition terminal.h:41
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
T between(T min, T sup)
Definition random.h:57
D_DOUBLE base(const value_t &v)
Definition real.h:35