Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-04-19 09:13:41

0001 // -*- C++ -*-
0002 //
0003 // This file is part of YODA -- Yet more Objects for Data Analysis
0004 // Copyright (C) 2008-2024 The YODA collaboration (see AUTHORS for details)
0005 //
0006 #ifndef YODA_POINT_H
0007 #define YODA_POINT_H
0008 
0009 #include "YODA/AnalysisObject.h"
0010 #include "YODA/Exceptions.h"
0011 #include "YODA/Transformation.h"
0012 #include "YODA/Utils/MathUtils.h"
0013 #include "YODA/Utils/PointUtils.h"
0014 #include "YODA/Utils/Traits.h"
0015 #include "YODA/Utils/sortedvector.h"
0016 #include "YODA/Utils/ndarray.h"
0017 #include <utility>
0018 #include <algorithm>
0019 #include <iostream>
0020 
0021 
0022 namespace YODA {
0023 
0024 
0025   /// Base class for all Point*Ds, providing generic access to their numerical properties
0026   class Point {
0027   public:
0028 
0029     using ValuePair = std::pair<double,double>;
0030 
0031 
0032     /// Virtual destructor for inheritance
0033     virtual ~Point() {};
0034 
0035 
0036     /// @name Core properties
0037     /// @{
0038 
0039     /// Space dimension of the point
0040     virtual size_t dim() const = 0;
0041 
0042     /// Get the point value for direction @a i
0043     virtual double val(size_t i) const = 0;
0044 
0045     /// Set the point value for direction @a i
0046     virtual void setVal(const size_t i, const double val) = 0;
0047 
0048     /// @}
0049 
0050 
0051     /// @name Errors
0052     /// @{
0053 
0054     /// Get error values for direction @a i
0055     //virtual const std::pair<double,double>& errs(size_t i) const = 0;
0056     /// Set symmetric error for direction @a i
0057     virtual void setErr(const size_t i, const double e) = 0;
0058 
0059     /// Set asymmetric error for direction @a i
0060     virtual void setErrs(const size_t i, const double eminus, const double eplus) = 0;
0061 
0062     /// Set error pair for direction @a i
0063     virtual void setErrs(const size_t i, const std::pair<double,double>& e) = 0;
0064 
0065     /// Get negative error value for direction @a i
0066     //virtual double errMinus(size_t i) const = 0;
0067     /// Set negative error for direction @a i
0068     virtual void setErrMinus(const size_t i, const double eminus) = 0;
0069 
0070     /// Get positive error value for direction @a i
0071     //virtual double errPlus(size_t i) const = 0;
0072     /// Set positive error for direction @a i
0073     virtual void setErrPlus(const size_t i, const double eplus) = 0;
0074 
0075     /// Get average error value for direction @a i
0076     //virtual double errAvg(size_t i) const = 0;
0077 
0078     // /// Get value minus negative error for direction @a i
0079     // double min(size_t i) const = 0;
0080     // /// Get value plus positive error for direction @a i
0081     // double max(size_t i) const = 0;*/
0082 
0083     /// @}
0084 
0085 
0086     /// @name Combined value and error setters
0087     /// @{
0088 
0089     /// Set value and symmetric error for direction @a i
0090     virtual void set(const size_t i, const double val, const double e) = 0;
0091 
0092     /// Set value and asymmetric error for direction @a i
0093     virtual void set(const size_t i, const double val, const double eminus, const double eplus) = 0;
0094 
0095     /// Set value and asymmetric error for direction @a i
0096     virtual void set(const size_t i, const double val, const std::pair<double,double>& e) = 0;
0097 
0098     /// @}
0099 
0100 
0101     /// @name Manipulations
0102     /// @{
0103 
0104     // /// Scaling of direction @a i
0105     virtual void scale(const size_t i, const double scale) = 0;
0106 
0107     //template<typename FN>
0108     //void scale(size_t i, FN f) = 0;
0109 
0110     /// @}
0111 
0112   };
0113 
0114 
0115 
0116 
0117 
0118   /// The base for an N-dimensional data point to be contained in a Scatter<N>
0119   template<size_t N>
0120   class PointBase : public Point {
0121   protected:
0122 
0123      // Convenient aliases
0124      using Pair = std::pair<double,double>;
0125      using ValList = std::initializer_list<double>;
0126      using PairList = std::initializer_list<Pair>;
0127 
0128      // extract the content type of an array Arr
0129      template<typename Arr>
0130      using containedType = std::decay_t<decltype(*std::declval<Arr>().begin())>;
0131 
0132      // check if content type of an array Arr is Pair
0133      template<typename Arr>
0134      using containsPair = typename std::is_same<containedType<Arr>, Pair>;
0135 
0136      // succeeds if T is an iterable container
0137      template<typename T>
0138      using isIterable = std::enable_if_t<Iterable<T>::value>;
0139 
0140      // succeeds if T is an iterable container and its content type is Pair
0141      template<typename T, typename U>
0142      using isIterableWithPair = std::enable_if_t<(Iterable<T>::value && Iterable<U>::value && containsPair<U>::value)>;
0143 
0144   public:
0145 
0146     // Typedefs
0147     using NdVal = typename Utils::ndarray<double, N>;
0148     using NdValPair = typename Utils::ndarray<std::pair<double,double>, N>;
0149     using DataSize = std::integral_constant<size_t, 3*N>;
0150 
0151 
0152     /// @name Constructors
0153     /// @{
0154 
0155     // @brief Default constructor
0156     PointBase() {
0157       clear();
0158     }
0159 
0160 
0161     /// @brief Constructor from position values without errors
0162     template <typename ValRange = ValList, typename = isIterable<ValRange>>
0163     PointBase(ValRange&& val) : _val(std::forward<ValRange>(val)) { }
0164 
0165 
0166     /// Constructor from values and a set of asymmetric errors
0167     template <typename ValRange = ValList,
0168               typename PairRange = PairList,
0169               typename = isIterableWithPair<ValRange,PairRange>>
0170     PointBase(ValRange&& val, PairRange&& errs)
0171       : _val(std::forward<ValRange>(val)), _errs(std::forward<PairRange>(errs)) { }
0172 
0173     /// Constructor from values and a set of symmetric errors
0174     template <typename ValRange = ValList, typename = isIterable<ValRange>>
0175     PointBase(ValRange&& val, ValRange&& errs)
0176        : _val(std::forward<ValRange>(val)) {
0177       if (val.size() != N || errs.size() != N)
0178         throw RangeError("Expected " + std::to_string(N) + " dimensions.");
0179       size_t i = 0;
0180       auto it = std::begin(errs);
0181       const auto& itEnd = std::end(errs);
0182       for (; it != itEnd; ++it) {
0183         _errs[i++] = std::make_pair(*it, *it);
0184       }
0185     }
0186 
0187 
0188     /// Constructor from values and a set of asymmetric errors
0189     template <typename ValRange = ValList, typename = isIterable<ValRange>>
0190     PointBase(ValRange&& val, ValRange&& errsdn, ValRange&& errsup)
0191        : _val(std::forward<ValRange>(val)) {
0192       if (val.size() != N || errsdn.size() != N || errsup.size() != N)
0193         throw RangeError("Expected " + std::to_string(N) + " dimensions.");
0194       size_t i = 0;
0195       auto itdn = std::begin(errsdn);
0196       auto itup = std::begin(errsup);
0197       const auto& itEnd = std::end(errsdn);
0198       for (; itdn != itEnd; ) {
0199         _errs[i++] = std::make_pair(*itdn++, *itup++);
0200       }
0201     }
0202 
0203     PointBase(const PointBase& p) : _val(p._val), _errs(p._errs) { }
0204 
0205     PointBase(PointBase&& p) : _val(std::move(p._val)), _errs(std::move(p._errs)) { }
0206 
0207     /// @}
0208 
0209     /// Space dimension of the point
0210     size_t dim() const { return N; }
0211 
0212     /// @name I/O
0213     /// @{
0214 
0215     void _renderYODA(std::ostream& os, const int width = 13) const noexcept {
0216 
0217       for (size_t i = 0; i < N; ++i) {
0218         os << std::setw(width) << std::left << _val[i] << "\t"
0219            << std::setw(width) << std::left << _errs[i].first  << "\t"
0220            << std::setw(width) << std::left << _errs[i].second << "\t";
0221       }
0222       os << "\n";
0223 
0224     }
0225 
0226     /// @}
0227 
0228     /// @name Modifiers
0229     /// @{
0230 
0231     /// Clear the point values and errors
0232     void clear() {
0233       for (size_t i = 0; i < N; ++i) {
0234         _val[i] = 0;
0235         _errs[i] = {0.,0.};
0236       }
0237     }
0238 
0239     /// Assignment operator
0240     PointBase& operator = (const PointBase& p) {
0241       if (this != &p) {
0242         _val = p._val;
0243         _errs = p._errs;
0244       }
0245       return *this;
0246     }
0247 
0248     /// Assignment operator
0249     PointBase& operator = (PointBase&& p) {
0250       if (this != &p) {
0251         _val = std::move(p._val);
0252         _errs = std::move(p._errs);
0253       }
0254       return *this;
0255     }
0256 
0257     /// @todo addError, addErrors, setErrors
0258 
0259     /// @}
0260 
0261 
0262   public:
0263 
0264     /// @name Coordinate accessors
0265     /// @{
0266 
0267     /// Get the coordinate vector
0268     NdVal& vals() { return _val; }
0269 
0270     /// Get the coordinate vector (const version)
0271     const NdVal& vals() const { return _val; }
0272 
0273     /// Get the value along direction @a i
0274     double val(size_t i) const {
0275       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0276       return _val[i];
0277     }
0278 
0279     /// Set the coordinate vector
0280     void setVal(const NdVal& val) {
0281       _val = val;
0282     }
0283 
0284     /// Set a specific coordinate
0285     void setVal(const size_t i, const double val) {
0286       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0287       _val[i] = val;
0288     }
0289 
0290     /// @}
0291 
0292 
0293     /// @name Error accessors
0294     /// @{
0295 
0296     /// Get error values
0297     NdValPair& errs() {
0298       return _errs;
0299     }
0300 
0301     /// Get error values (const version)
0302     const NdValPair& errs() const {
0303       return _errs;
0304     }
0305 
0306     /// Get error values along axis @a i
0307     Pair errs(const size_t i) const {
0308       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0309       return _errs[i];
0310     }
0311 
0312     /// Get the minus error along axis @a i
0313     double errMinus(const size_t i) const {
0314       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0315       return _errs[i].first;
0316     }
0317 
0318     /// Get the plus error along axis @a i
0319     double errPlus(const size_t i) const {
0320       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0321       return _errs[i].second;
0322     }
0323 
0324     // Get the average error along axis @a i
0325     double errAvg(const size_t i) const {
0326       return 0.5*(errMinus(i) + errPlus(i));
0327     }
0328 
0329     /// Get value minus negative error along axis @a i
0330     double min(const size_t i) const {
0331       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0332       return _val[i] - _errs[i].first;
0333     }
0334 
0335     /// Get value plus positive error along axis @a i
0336     double max(const size_t i) const {
0337       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0338       return _val[i] + _errs[i].second;
0339     }
0340 
0341     /// Set a symmetric error pair along axis @a i
0342     void setErr(const size_t i, const double e) {
0343       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0344       const double err = fabs(e);
0345       _errs[i] = { err, err};
0346     }
0347 
0348     /// Set an asymmetric error pair along axis @a i
0349     void setErrs(const size_t i, const double eminus, const double eplus) {
0350       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0351       _errs[i] = { eminus, eplus};
0352     }
0353 
0354     /// Set a specific error pair along axis @a i
0355     void setErrs(const size_t i, const std::pair<double,double>& e) {
0356       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0357       _errs[i] = e;
0358     }
0359 
0360     /// Set a specific minus error along axis @a i
0361     void setErrMinus(const size_t i, const double eminus) {
0362       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0363       _errs[i].first = eminus;
0364     }
0365 
0366     /// Set a specific plus error along axis @a i
0367     void setErrPlus(const size_t i, const double eplus) {
0368       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0369       _errs[i].second = eplus;
0370     }
0371 
0372     /// @}
0373 
0374     /// @name Combined value and error setters
0375     /// @{
0376 
0377     void set(const size_t i, const double val, const double e) {
0378       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0379       const double err = fabs(e);
0380       _val[i] = val;
0381       _errs[i] = {err,err};
0382     }
0383 
0384     void set(const size_t i, const double val, const double eminus, const double eplus) {
0385       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0386       _val[i] = val;
0387       _errs[i].first = eminus;
0388       _errs[i].second = eplus;
0389     }
0390 
0391     void set(const size_t i, const double val, const std::pair<double,double>& e) {
0392       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0393       _val[i] = val;
0394       _errs[i] = e;
0395     }
0396 
0397     /// @}
0398 
0399     /// @name Scaling and transformations
0400     /// @{
0401 
0402     /// Scaling value along direction @a i
0403     void scaleVal(const size_t i, const double scale) {
0404       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0405       _val[i] *= scale;
0406     }
0407 
0408     /// Scaling error along direction @a i
0409     void scaleErr(const size_t i, const double scale) {
0410       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0411       _errs[i].first  *= scale;
0412       _errs[i].second *= scale;
0413     }
0414 
0415     /// Scaling along direction @a i
0416     void scale(const size_t i, const double scale) {
0417       scaleVal(i, scale);
0418       scaleErr(i, scale);
0419     }
0420 
0421     /// Uniform scaling
0422     void scale(const NdVal& scales) {
0423       for (size_t i = 0; i < N; ++i) {
0424         scale(i, scales[i]);
0425       }
0426     }
0427 
0428     /// Generalised transformations with functors
0429     void scale(const Trf<N>& trf) {
0430       trf.transform(_val, _errs);
0431     }
0432 
0433     /// Generalised transformations with functors
0434     /// along axis @a i
0435     void scale(const size_t i, const Trf<N>& trf) {
0436       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0437       trf.transform(_val[i], _errs[i]);
0438     }
0439     //
0440     void transform(const size_t i, const Trf<N>& trf) {
0441       scale(i, trf);
0442     }
0443 
0444     /// @}
0445 
0446     /// @name MPI (de-)serialisation
0447     //@{
0448 
0449     std::vector<double> _serializeContent() const noexcept {
0450       std::vector<double> rtn;
0451       rtn.reserve(DataSize::value);
0452       rtn.insert(rtn.end(), _val.begin(),  _val.end());
0453       for (auto err : _errs) {
0454         rtn.push_back(std::move(err.first));
0455         rtn.push_back(std::move(err.second));
0456       }
0457       return rtn;
0458     }
0459 
0460     void _deserializeContent(const std::vector<double>& data) {
0461 
0462       if (data.size() != DataSize::value)
0463         throw UserError("Length of serialized data should be "+std::to_string(DataSize::value)+"!");
0464 
0465       for (size_t i = 0; i < N; ++i) {
0466         _val[i] = data[i];
0467         _errs[i] = { data[N+i], data[2*N+i] };
0468       }
0469 
0470     }
0471 
0472     // @}
0473 
0474   protected:
0475 
0476     /// @name Value and error variables
0477     /// @{
0478 
0479     NdVal _val;
0480     NdValPair _errs;
0481 
0482     /// @}
0483 
0484   };
0485 
0486 
0487 
0488   /// @name Comparison operators
0489   /// @{
0490 
0491   /// Equality test
0492   template <size_t N>
0493   inline bool operator==(const PointBase<N>& a, const PointBase<N>& b) {
0494     // Compare valitions
0495     for (size_t i = 0; i < N; ++i) {
0496       if ( !fuzzyEquals(a.vals()[i],        b.vals()[i]) )        return false;
0497       if ( !fuzzyEquals(a.errs()[i].first,  b.errs()[i].first))  return false;
0498       if ( !fuzzyEquals(a.errs()[i].second, b.errs()[i].second)) return false;
0499     }
0500     return true;
0501   }
0502 
0503   /// Inequality test
0504   template <size_t N>
0505   inline bool operator!=(const PointBase<N>& a, const PointBase<N>& b) {
0506     return !(a == b);
0507   }
0508 
0509 
0510   /// Less-than operator used to sort points
0511   template <size_t N>
0512   inline bool operator<(const PointBase<N>& a, const PointBase<N>& b) {
0513     #define LT_IF_NOT_EQ(a,b) { if (!fuzzyEquals(a, b)) return a < b; }
0514     for (size_t i = 0; i < N; ++i) {
0515       LT_IF_NOT_EQ(a.vals()[i],        b.vals()[i]);
0516       LT_IF_NOT_EQ(a.errs()[i].first,  b.errs()[i].first);
0517       LT_IF_NOT_EQ(a.errs()[i].second, b.errs()[i].second);
0518     }
0519     #undef LT_IF_NOT_EQ
0520     return false;
0521   }
0522 
0523   /// Less-than-or-equals operator used to sort points
0524   template <size_t N>
0525   inline bool operator<=(const PointBase<N>& a, const PointBase<N>& b) {
0526     if (a == b) return true;
0527     return a < b;
0528   }
0529 
0530   /// Greater-than operator used to sort points
0531   template <size_t N>
0532   inline bool operator>(const PointBase<N>& a, const PointBase<N>& b) {
0533     return !(a <= b);
0534   }
0535 
0536   /// Greater-than-or-equals operator used to sort points
0537   template <size_t N>
0538   inline bool operator>=(const PointBase<N>& a, const PointBase<N>& b) {
0539     return !(a < b);
0540   }
0541 
0542   /// @}
0543 
0544   template <size_t N>
0545   class PointND : public PointBase<N> {
0546     using BaseT = PointBase<N>;
0547     using BaseT::BaseT;
0548   };
0549 
0550 
0551 
0552   /// A 1D data point to be contained in a Scatter1D
0553   template <>
0554   class PointND<1> : public PointBase<1>,
0555                      public XDirectionMixin<PointND<1>> {
0556   public:
0557 
0558     using BaseT = PointBase<1>;
0559     using BaseT::BaseT;
0560 
0561     /// @name Constructors
0562     /// @{
0563 
0564     /// Constructor from values with optional symmetric errors
0565     PointND(double x, double ex=0.0)
0566       : BaseT({x}, {{ex, ex}}) {  }
0567 
0568 
0569     /// Constructor from values with explicit asymmetric errors
0570     PointND(double x, double exminus, double explus)
0571       : BaseT({x}, {{exminus, explus}}) {  }
0572 
0573 
0574     /// Constructor from values with asymmetric errors
0575     PointND(double x, const std::pair<double,double>& ex)
0576       : BaseT( {x}, {ex}) {  }
0577 
0578 
0579     /// Copy constructor
0580     PointND(const BaseT& other) : BaseT(other) {}
0581 
0582     /// Move constructor
0583     PointND(BaseT&& other) : BaseT(std::move(other)) {}
0584 
0585     /// @}
0586 
0587 
0588     /// @name Coordinate accessors
0589     /// @{
0590 
0591     /// Get the value along direction @a i
0592     double val(size_t i=0) const {
0593       if (i >= 1) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0594       return _val[i];
0595     }
0596 
0597     /// @}
0598 
0599   };
0600 
0601 
0602 
0603   /// A 2D data point to be contained in a Scatter2D
0604   template <>
0605   class PointND<2> : public PointBase<2>,
0606                      public XDirectionMixin<PointND<2>>,
0607                      public YDirectionMixin<PointND<2>> {
0608   public:
0609 
0610     using BaseT = PointBase<2>;
0611     using BaseT::BaseT;
0612 
0613     /// @name Constructors
0614     /// @{
0615 
0616     /// Constructor from values with optional symmetric errors
0617     PointND(double x, double y, double ex=0.0, double ey=0.0)
0618       : BaseT( {x,y}, {{ex,ex}, {ey,ey}}) {  }
0619 
0620 
0621     /// Constructor from values with explicit asymmetric errors
0622     PointND(double x, double y,
0623                  double exminus, double explus,
0624                  double eyminus, double eyplus)
0625       : BaseT({x,y}, {{exminus,explus}, {eyminus,eyplus}}) {  }
0626 
0627 
0628     /// Constructor from values with asymmetric errors on both x and y
0629     PointND(double x, double y, const std::pair<double,double>& ex, const std::pair<double,double>& ey)
0630       : BaseT({x,y}, {ex, ey}) {  }
0631 
0632 
0633     /// Copy constructor
0634     PointND(const BaseT& other) : BaseT(other) { }
0635 
0636     /// Move constructor
0637     PointND(BaseT&& other) : BaseT(std::move(other)) { }
0638 
0639     /// @}
0640 
0641 
0642     // @name Manipulations
0643     /// @{
0644 
0645     /// Scaling of both axes
0646     void scaleXY(double scalex, double scaley) {
0647       scaleX(scalex);
0648       scaleY(scaley);
0649     }
0650 
0651     /// @}
0652 
0653   };
0654 
0655 
0656 
0657   /// A 3D data point to be contained in a Scatter3D
0658   template <>
0659   class PointND<3> : public PointBase<3>,
0660                      public XDirectionMixin<PointND<3>>,
0661                      public YDirectionMixin<PointND<3>>,
0662                      public ZDirectionMixin<PointND<3>> {
0663   public:
0664 
0665     using BaseT = PointBase<3>;
0666     using BaseT::BaseT;
0667 
0668     /// @name Constructors
0669     /// @{
0670 
0671     /// Constructor from values with optional symmetric errors
0672     PointND(double x, double y, double z, double ex=0.0, double ey=0.0, double ez=0.0)
0673       : BaseT({x,y,z}, {{ex,ex}, {ey,ey}, {ez,ez}}) { }
0674 
0675 
0676     /// Constructor from values with explicit asymmetric errors
0677     PointND(double x, double y, double z,
0678                  double exminus, double explus,
0679                  double eyminus, double eyplus,
0680                  double ezminus, double ezplus)
0681       : BaseT({x,y,z}, {{exminus,explus}, {eyminus,eyplus}, {ezminus,ezplus}}) { }
0682 
0683     /// Constructor from asymmetric errors given as vectors
0684     PointND(double x, double y, double z,
0685                  const std::pair<double,double>& ex,
0686                  const std::pair<double,double>& ey,
0687                  const std::pair<double,double>& ez)
0688       : BaseT({x,y,z}, {ex,ey,ez}) {  }
0689 
0690 
0691     /// Copy constructor
0692     PointND(const BaseT& other) : BaseT(other) { }
0693 
0694     /// Move constructor
0695     PointND(BaseT&& other) : BaseT(std::move(other)) { }
0696 
0697     /// @}
0698 
0699     // @name Manipulations
0700     /// @{
0701 
0702     /// Scaling of both axes
0703     void scaleXYZ(double scalex, double scaley, double scalez) {
0704       scaleX(scalex);
0705       scaleY(scaley);
0706       scaleZ(scalez);
0707     }
0708 
0709     /// @}
0710 
0711   };
0712 
0713 
0714   /// @brief User-familiar alias
0715   using Point1D = PointND<1>;
0716   using Point2D = PointND<2>;
0717   using Point3D = PointND<3>;
0718   using Point4D = PointND<4>;
0719 
0720 }
0721 
0722 #endif