sigma  1.0.0
Loading...
Searching...
No Matches
affine.hpp
Go to the documentation of this file.
1#pragma once
2#include <atomic>
3#include <optional>
5#include <sstream>
6#include <unordered_map>
7
11
12namespace sigma {
13
47template<typename ValueType>
48class Affine {
49public:
51 using size_type = std::size_t;
52
54 using value_t = ValueType;
55
58
60 using error_terms_t = std::unordered_map<error_term_t, value_t>;
61
64
65 // --- Constructors and Assignment ----------------------------------------
66
73 Affine() = default;
74
85
99 Affine(value_t lo, value_t hi) : Affine(interval_t(lo, hi)) {}
100
113 explicit Affine(const interval_t& interval) {
114 if(interval.empty()) { return; }
115 m_center_ = interval.median();
116 if(interval.radius() > 0) {
117 m_error_terms_[make_error_term()] = interval.radius();
118 }
119 }
120
134 m_center_(center), m_error_terms_(std::move(radii)) {}
135
143 Affine(const Affine& other) = default;
144
151 Affine(Affine&& other) noexcept = default;
152
162 Affine& operator=(const Affine& other) = default;
163
172 Affine& operator=(Affine&& other) noexcept = default;
173
174 // -- State Accessors -----------------------------------------------------
175
186
194 value_t center() const {
195 assert_not_empty_();
196 return *m_center_;
197 }
198
213 const error_terms_t& error_terms() const { return m_error_terms_; }
214
225
236 void set_center(value_t center) { m_center_ = center; }
237
252 if(empty()) set_center(value_t{0});
253 m_error_terms_[error_term] = radius;
254 }
255
268 bool contains(value_t value) const;
269
284 bool contains(const interval_t& interval) const;
285
299 bool contains(const Affine& affine) const {
300 return contains(affine.range());
301 }
302
313 bool empty() const noexcept { return !m_center_; }
314
327 std::string print_affine_form() const;
328
343 std::string print_interval_form() const {
344 return range().print_interval_form();
345 }
346
347 // -- Arithmetic Operators ------------------------------------------------
348
362
376 if(empty()) { return *this = Affine(value); }
377 (*m_center_) += value;
378 return *this;
379 }
380
398 Affine& operator+=(const Affine& other);
399
413 Affine operator+(value_t value) const { return Affine(*this) += value; }
414
428 Affine operator+(const Affine& other) const {
429 return Affine(*this) += other;
430 }
431
445 if(empty()) { return *this = Affine(-value); }
446 (*m_center_) -= value;
447 return *this;
448 }
449
467 Affine& operator-=(const Affine& other);
468
482 Affine operator-(value_t value) const { return Affine(*this) -= value; }
483
497 Affine operator-(const Affine& other) const {
498 return Affine(*this) -= other;
499 }
500
516 if(empty()) { return *this; }
517 (*m_center_) *= value;
518 for(auto&& [error_symbol, error_term_i] : m_error_terms_) {
519 error_term_i *= value;
520 }
521 return *this;
522 }
523
546 Affine& operator*=(const Affine& other);
547
561 Affine operator*(value_t value) const { return Affine(*this) *= value; }
562
576 Affine operator*(const Affine& other) const {
577 return Affine(*this) *= other;
578 }
579
596 if(value == 0) { throw std::domain_error("Division by zero"); }
597 return *this *= value_t(1.0 / value);
598 }
599
616 Affine& operator/=(const Affine& other);
617
630 Affine operator/(value_t value) const { return Affine(*this) /= value; }
631
647 Affine operator/(const Affine& other) const {
648 return Affine(*this) /= other;
649 }
650
674 value_t delta) const {
675 value_t new_center = empty() ? zeta : alpha * center() + zeta;
676 error_terms_t new_error_terms;
677 for(auto&& [error_symbol, error_term_i] : m_error_terms_) {
678 new_error_terms[error_symbol] = alpha * error_term_i;
679 }
680 new_error_terms[make_error_term()] = delta;
681 return Affine(new_center, std::move(new_error_terms));
682 }
683
708
709 // -- Comparison Operators ------------------------------------------------
710
726 bool operator==(const Affine& other) const {
727 if(empty() != other.empty()) { return false; }
728 if(empty()) { return true; }
729 if(m_center_ != other.m_center_) { return false; }
730 return m_error_terms_ == other.m_error_terms_;
731 }
732
748 bool operator!=(const Affine& other) const { return !(*this == other); }
749
752 static std::atomic<size_type> s_next_id{0};
753 return s_next_id.fetch_add(1);
754 }
755
756private:
758 void assert_not_empty_() const {
759 if(empty()) { throw std::domain_error("Affine form is empty"); }
760 }
761
764 std::optional<value_t> m_center_;
765
768 error_terms_t m_error_terms_;
769};
770
771// -- Non-member functions
772// ---------------------------------------------------
773
790template<typename ValueType>
791std::ostream& operator<<(std::ostream& os, const Affine<ValueType>& a) {
792 os << a.range();
793 return os;
794}
795
814template<typename ValueType>
816 return a * value;
817}
818
819// -- Out-of-line definitions
820// ------------------------------------------------
821
822template<typename ValueType>
824 if(empty()) { return interval_t(); }
825 auto r = radius();
826 return interval_t(center() - r, center() + r);
827}
828
829template<typename ValueType>
831 assert_not_empty_();
832 value_t r = 0;
833 for(auto&& [error_symbol, error_term_i] : m_error_terms_) {
834 r += std::fabs(error_term_i);
835 }
836 return r;
837}
838
839template<typename ValueType>
840auto Affine<ValueType>::contains(value_t value) const -> bool {
841 if(empty()) { return false; }
842 return range().contains(value);
843}
844
845template<typename ValueType>
846auto Affine<ValueType>::contains(const interval_t& interval) const -> bool {
847 if(interval.empty()) { return true; }
848 if(empty()) { return false; }
849 return range().contains(interval);
850}
851
852template<typename ValueType>
854 if(empty()) { return "∅"; }
855 std::stringstream ss;
856 ss << center();
857 for(auto&& [error_symbol, error_term_i] : m_error_terms_) {
858 ss << " +/- " << error_term_i;
859 }
860 return ss.str();
861}
862
863template<typename ValueType>
865 if(empty()) { return *this; }
866 value_t new_center = -center();
867 error_terms_t new_error_terms;
868 for(auto&& [error_symbol, error_term_i] : m_error_terms_) {
869 new_error_terms[error_symbol] = -error_term_i;
870 }
871 return Affine(new_center, new_error_terms);
872}
873
874template<typename ValueType>
876 if(empty()) { return *this = other; }
877 if(other.empty()) { return *this; }
878 value_t new_center = center() + other.center();
879 error_terms_t new_error_terms = m_error_terms_;
880 for(auto&& [error_symbol, error_term_i] : other.m_error_terms_) {
881 new_error_terms[error_symbol] += error_term_i;
882 }
883 return *this = Affine(new_center, new_error_terms);
884}
885
886template<typename ValueType>
888 return *this += -other;
889}
890
891template<typename ValueType>
893 if(empty() || other.empty()) { return *this = Affine(); }
894 value_t new_center = center() * other.center();
895 error_terms_t new_error_terms;
896 value_t new_radius = 0;
897 for(auto&& [error_symbol, error_term_i] : m_error_terms_) {
898 new_error_terms[error_symbol] = error_term_i * other.center();
899 new_radius += std::fabs(new_error_terms[error_symbol]);
900 }
901 for(auto&& [error_symbol, error_term_j] : other.m_error_terms_) {
902 new_error_terms[error_symbol] += error_term_j * center();
903 new_radius += std::fabs(new_error_terms[error_symbol]);
904 }
905 auto correction = radius() * other.radius();
906 new_error_terms[make_error_term()] = correction;
907 return *this = Affine(new_center, new_error_terms);
908}
909
910template<typename ValueType>
912 // Multiply by this by 1 / other
913 return *this *= other.multiplicative_inverse();
914}
915
916template<typename ValueType>
918 assert_not_empty_();
919 if(contains(0)) { throw std::domain_error("Division by zero"); }
920 // Compute the affine transformation which transforms other to 1 / other
921 auto other_range = range();
922 auto abs_inf = std::fabs(other_range.lower());
923 auto abs_sup = std::fabs(other_range.upper());
924 auto a = std::min(abs_inf, abs_sup);
925 auto b = std::max(abs_inf, abs_sup);
926 auto alpha = value_t(-1.0) / (b * b);
927 auto lo = value_t(1.0) / a - alpha * a;
928 auto hi = value_t(2.0) / b;
929 interval_t interval(std::min(lo, hi), std::max(lo, hi));
930 auto zeta = std::fabs(interval.median());
931 auto delta = interval.radius();
932 return apply_affine_transform(alpha, zeta, delta);
933}
934
937
940
941} // namespace sigma
942
Components for compatibility with Eigen.
Convenience header for affine form operations.
Implements affine arithmetic.
Definition affine.hpp:48
Affine operator+(const Affine &other) const
Returns the sum of *this and other.
Definition affine.hpp:428
Affine & operator/=(value_t value)
Overwrites *this with the quotient of *this and value.
Definition affine.hpp:595
Affine & operator-=(value_t value)
Overwrites *this with the difference of *this and value.
Definition affine.hpp:444
bool contains(const Affine &affine) const
Checks if affine is contained within *this.
Definition affine.hpp:299
Affine< ValueType > operator*(ValueType value, const Affine< ValueType > &a)
Multiplies a scalar by an affine form.
Definition affine.hpp:815
Affine & operator-=(const Affine &other)
Overwrites *this with the difference of *this and other.
Definition affine.hpp:887
Affine & operator+=(value_t value)
Overwrites *this with the sum of *this and value.
Definition affine.hpp:375
Affine(value_t center, error_terms_t radii)
Construct an affine form from a center value and a map of errors.
Definition affine.hpp:133
bool operator==(const Affine &other) const
Checks if *this and other represent the same affine form.
Definition affine.hpp:726
Affine operator+(value_t value) const
Returns the sum of *this and value.
Definition affine.hpp:413
Affine apply_affine_transform(value_t alpha, value_t zeta, value_t delta) const
Applies an affine transformation to *this.
Definition affine.hpp:673
Affine operator/(value_t value) const
Returns the quotient of *this and value.
Definition affine.hpp:630
bool contains(const interval_t &interval) const
Checks if interval is contained within *this.
Definition affine.hpp:846
Affine()=default
Constructs an empty affine form.
ValueType value_t
Type used for storing floating point values.
Definition affine.hpp:54
Affine(Affine &&other) noexcept=default
Constructs an affine form by moving other.
std::unordered_map< error_term_t, value_t > error_terms_t
Type used to map error terms to their radii.
Definition affine.hpp:60
std::string print_interval_form() const
Creates a string of the interval form of *this.
Definition affine.hpp:343
Affine(value_t lo, value_t hi)
Constructs an affine form from a lower and upper bound.
Definition affine.hpp:99
const error_terms_t & error_terms() const
Returns the error terms of the affine form.
Definition affine.hpp:213
Affine multiplicative_inverse() const
Returns the multiplicative inverse of *this.
Definition affine.hpp:917
Affine(value_t center)
Constructs an affine form from a center value.
Definition affine.hpp:84
std::string print_affine_form() const
Creates a string of the affine form of *this.
Definition affine.hpp:853
value_t center() const
Definition affine.hpp:194
Affine(const Affine &other)=default
Makes a deep copy of other.
Affine operator-(value_t value) const
Returns the difference of *this and value.
Definition affine.hpp:482
size_type error_term_t
Opaque type used to store error term information.
Definition affine.hpp:57
void set_center(value_t center)
Sets the center of the affine form.
Definition affine.hpp:236
Interval< value_t > interval_t
Type of an interval.
Definition affine.hpp:63
Affine operator*(const Affine &other) const
Returns the product of *this and other.
Definition affine.hpp:576
Affine & operator*=(value_t value)
Overwrites *this with the product of *this and value.
Definition affine.hpp:515
static error_term_t make_error_term()
Returns a process-wide unique integer ID for a new error term symbol.
Definition affine.hpp:751
bool operator!=(const Affine &other) const
Checks if *this and other represent different affine forms.
Definition affine.hpp:748
Affine & operator*=(const Affine &other)
Overwrites *this with the product of *this and other.
Definition affine.hpp:892
Affine & operator=(Affine &&other) noexcept=default
Moves the value of other to this affine form.
std::size_t size_type
Type used for indexing and offsets.
Definition affine.hpp:51
Affine operator-(const Affine &other) const
Returns the difference of *this and other.
Definition affine.hpp:497
void add_error_term(error_term_t error_term, value_t radius)
Adds an error term to the affine form.
Definition affine.hpp:251
Affine operator*(value_t value) const
Returns the product of *this and value.
Definition affine.hpp:561
bool empty() const noexcept
Checks if this affine form is representing an empty interval.
Definition affine.hpp:313
std::ostream & operator<<(std::ostream &os, const Affine< ValueType > &a)
Outputs the range of an affine form to an output stream.
Definition affine.hpp:791
Affine & operator/=(const Affine &other)
Overwrites *this with the quotient of *this and other.
Definition affine.hpp:911
interval_t range() const
Returns the interval represented by the affine form.
Definition affine.hpp:823
bool contains(value_t value) const
Checks if the interval represented by *this contains value.
Definition affine.hpp:840
Affine operator/(const Affine &other) const
Returns the quotient of *this and other.
Definition affine.hpp:647
Affine & operator+=(const Affine &other)
Overwrites *this with the sum of *this and other.
Definition affine.hpp:875
Affine(const interval_t &interval)
Constructs an affine form from an interval.
Definition affine.hpp:113
Affine operator-() const
Returns the additive inverse of *this.
Definition affine.hpp:864
Affine & operator=(const Affine &other)=default
Assigns the value of other to this affine form.
value_t radius() const
Returns the radius of the affine form.
Definition affine.hpp:830
Models a numeric interval.
Definition interval.hpp:23
value_t median() const
Returns the midpoint of the interval.
Definition interval.hpp:82
std::string print_interval_form() const
Print the interval in interval form.
Definition interval.hpp:604
value_t radius() const
Returns the half-width of the interval.
Definition interval.hpp:96
bool empty() const
Is *this the empty interval?
Definition interval.hpp:106
Defines the Interval class.
The primary namespace for the sigma library.
Definition affine.hpp:12
Affine< double > ADouble
Typedef for an affine form of doubles.
Definition affine.hpp:939
Affine< float > AFloat
Typedef for an affine form of floats.
Definition affine.hpp:936