Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:54:49

0001 //----------------------------------*-C++-*----------------------------------//
0002 // Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers.
0003 // See the top-level COPYRIGHT file for details.
0004 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0005 //---------------------------------------------------------------------------//
0006 //! \file corecel/math/SoftEqual.hh
0007 //---------------------------------------------------------------------------//
0008 #pragma once
0009 
0010 #include <cmath>
0011 
0012 #include "corecel/Assert.hh"
0013 #include "corecel/Macros.hh"
0014 #include "corecel/Types.hh"
0015 
0016 #include "detail/SoftEqualTraits.hh"
0017 
0018 namespace celeritas
0019 {
0020 //---------------------------------------------------------------------------//
0021 /*!
0022  * Square root of the soft equivalence tolerance for Celeritas.
0023  *
0024  * This tolerance is needed for operations where the accuracy is limited by the
0025  * square root of machine precision.
0026  *
0027  * \todo Move orange tolerance and related operations into corecel/math
0028  * alongside this, revisit ArrayUtils soft comparisons.
0029  */
0030 CELER_CONSTEXPR_FUNCTION real_type sqrt_tol()
0031 {
0032     return detail::SoftEqualTraits<real_type>::sqrt_prec();
0033 }
0034 
0035 //---------------------------------------------------------------------------//
0036 /*!
0037  * Functor for noninfinite floating point equality.
0038  *
0039  * This function-like class considers an \em absolute tolerance for values near
0040  * zero, and a \em relative tolerance for values far from zero. It correctly
0041  * returns "false" if either value being compared is NaN.  The call operator is
0042  * \em commutative: \c eq(a,b) should always give the same as \c eq(b,a).
0043  *
0044  * The actual comparison is: \f[
0045  |a - b| < \max(\epsilon_r \max(|a|, |b|), \epsilon_a)
0046  \f]
0047  *
0048  * \note The edge case where both values are infinite (with the same sign)
0049  * returns *false* for equality, which could be considered reasonable because
0050  * relative error is meaningless. To explicitly allow infinities to compare
0051  * equal, you must test separately, e.g., `a == b || soft_eq(a, b)`.
0052  */
0053 template<class RealType = ::celeritas::real_type>
0054 class SoftEqual
0055 {
0056   public:
0057     //!@{
0058     //! \name Type aliases
0059     using value_type = RealType;
0060     //!@}
0061 
0062   public:
0063     // Construct with default relative/absolute precision
0064     CELER_CONSTEXPR_FUNCTION SoftEqual();
0065 
0066     // Construct with default absolute precision
0067     explicit CELER_FUNCTION SoftEqual(value_type rel);
0068 
0069     // Construct with both relative and absolute precision
0070     CELER_FUNCTION SoftEqual(value_type rel, value_type abs);
0071 
0072     //// COMPARISON ////
0073 
0074     // Compare two values (implicitly casting arguments)
0075     bool CELER_FUNCTION operator()(value_type a, value_type b) const;
0076 
0077     //// ACCESSORS ////
0078 
0079     //! Relative allowable error
0080     CELER_CONSTEXPR_FUNCTION value_type rel() const { return rel_; }
0081 
0082     //! Absolute tolerance
0083     CELER_CONSTEXPR_FUNCTION value_type abs() const { return abs_; }
0084 
0085   private:
0086     value_type rel_;
0087     value_type abs_;
0088 
0089     using SETraits = detail::SoftEqualTraits<value_type>;
0090 };
0091 
0092 //---------------------------------------------------------------------------//
0093 /*!
0094  * Compare for equality before checking with the given functor.
0095  *
0096  * This CRTP class allows \c SoftEqual to work for infinities.
0097  */
0098 template<class F>
0099 class EqualOr : public F
0100 {
0101   public:
0102     //! Forward arguments to parent class
0103     template<class... C>
0104     CELER_FUNCTION EqualOr(C&&... args) : F{std::forward<C>(args)...}
0105     {
0106     }
0107 
0108     //! Forward arguments to comparison operator after comparing
0109     template<class T, class U>
0110     bool CELER_FUNCTION operator()(T a, U b) const
0111     {
0112         return a == b || static_cast<F const&>(*this)(a, b);
0113     }
0114 };
0115 
0116 //---------------------------------------------------------------------------//
0117 /*!
0118  * Functor for floating point equality.
0119  */
0120 template<class RealType = ::celeritas::real_type>
0121 class SoftZero
0122 {
0123   public:
0124     //!@{
0125     //! \name Type aliases
0126     using argument_type = RealType;
0127     using value_type = RealType;
0128     //!@}
0129 
0130   public:
0131     // Construct with default relative/absolute precision
0132     CELER_CONSTEXPR_FUNCTION SoftZero();
0133 
0134     // Construct with absolute precision
0135     explicit CELER_FUNCTION SoftZero(value_type abs);
0136 
0137     //// COMPARISON ////
0138 
0139     // Compare the given value to zero
0140     inline CELER_FUNCTION bool operator()(value_type actual) const;
0141 
0142     //// ACCESSORS ////
0143 
0144     //! Absolute tolerance
0145     CELER_CONSTEXPR_FUNCTION value_type abs() const { return abs_; }
0146 
0147   private:
0148     value_type abs_;
0149 
0150     using SETraits = detail::SoftEqualTraits<value_type>;
0151 };
0152 
0153 //---------------------------------------------------------------------------//
0154 // TEMPLATE DEDUCTION GUIDES
0155 //---------------------------------------------------------------------------//
0156 template<class T>
0157 CELER_FUNCTION SoftEqual(T) -> SoftEqual<T>;
0158 template<class T>
0159 CELER_FUNCTION SoftEqual(T, T) -> SoftEqual<T>;
0160 template<class F>
0161 CELER_FUNCTION EqualOr(F&&) -> EqualOr<F>;
0162 
0163 //---------------------------------------------------------------------------//
0164 // INLINE DEFINITIONS
0165 //---------------------------------------------------------------------------//
0166 /*!
0167  * Construct with default relative/absolute precision.
0168  */
0169 template<class RealType>
0170 CELER_CONSTEXPR_FUNCTION SoftEqual<RealType>::SoftEqual()
0171     : rel_{SETraits::rel_prec()}, abs_{SETraits::abs_thresh()}
0172 {
0173 }
0174 
0175 //---------------------------------------------------------------------------//
0176 /*!
0177  * Construct with scaled absolute precision.
0178  */
0179 template<class RealType>
0180 CELER_FUNCTION SoftEqual<RealType>::SoftEqual(value_type rel)
0181     : SoftEqual(rel, rel * (SETraits::abs_thresh() / SETraits::rel_prec()))
0182 {
0183     CELER_EXPECT(rel > 0);
0184 }
0185 
0186 //---------------------------------------------------------------------------//
0187 /*!
0188  * Construct with both relative and absolute precision.
0189  *
0190  * \param rel tolerance of relative error (default 1.0e-12 for doubles)
0191  *
0192  * \param abs threshold for absolute error when comparing small quantities
0193  *           (default 1.0e-14 for doubles)
0194  */
0195 template<class RealType>
0196 CELER_FUNCTION SoftEqual<RealType>::SoftEqual(value_type rel, value_type abs)
0197     : rel_{rel}, abs_{abs}
0198 {
0199     CELER_EXPECT(rel > 0);
0200     CELER_EXPECT(abs > 0);
0201 }
0202 
0203 //---------------------------------------------------------------------------//
0204 /*!
0205  * Compare two values, implicitly casting arguments.
0206  */
0207 template<class RealType>
0208 CELER_FUNCTION bool
0209 SoftEqual<RealType>::operator()(value_type a, value_type b) const
0210 {
0211     real_type rel = rel_ * std::fmax(std::fabs(a), std::fabs(b));
0212     return std::fabs(a - b) < std::fmax(abs_, rel);
0213 }
0214 
0215 //---------------------------------------------------------------------------//
0216 /*!
0217  * Construct with default relative/absolute precision.
0218  */
0219 template<class RealType>
0220 CELER_CONSTEXPR_FUNCTION SoftZero<RealType>::SoftZero()
0221     : abs_(SETraits::abs_thresh())
0222 {
0223 }
0224 
0225 //---------------------------------------------------------------------------//
0226 /*!
0227  * Construct with specified precision.
0228  *
0229  * \param abs threshold for absolute error (default 1.0e-14 for doubles)
0230  */
0231 template<class RealType>
0232 CELER_FUNCTION SoftZero<RealType>::SoftZero(value_type abs) : abs_(abs)
0233 {
0234     CELER_EXPECT(abs > 0);
0235 }
0236 
0237 //---------------------------------------------------------------------------//
0238 /*!
0239  * See if the value is within absolute tolerance of zero.
0240  *
0241  * \param actual scalar floating point value
0242  */
0243 template<class RealType>
0244 CELER_FUNCTION bool SoftZero<RealType>::operator()(value_type actual) const
0245 {
0246     return std::fabs(actual) < abs_;
0247 }
0248 
0249 //---------------------------------------------------------------------------//
0250 //! Soft equivalence with default tolerance
0251 template<class RealType>
0252 inline CELER_FUNCTION bool soft_equal(RealType expected, RealType actual)
0253 {
0254     return SoftEqual<RealType>()(expected, actual);
0255 }
0256 
0257 //---------------------------------------------------------------------------//
0258 //! Soft equivalence with relative error
0259 template<class RealType>
0260 inline CELER_FUNCTION bool
0261 soft_near(RealType expected, RealType actual, RealType rel_error)
0262 {
0263     return SoftEqual<RealType>(rel_error)(expected, actual);
0264 }
0265 
0266 //---------------------------------------------------------------------------//
0267 //! Soft equivalence to zero, with default tolerance
0268 template<class RealType>
0269 inline CELER_FUNCTION bool soft_zero(RealType actual)
0270 {
0271     return SoftZero<RealType>()(actual);
0272 }
0273 
0274 //---------------------------------------------------------------------------//
0275 //! Soft modulo operator
0276 template<class RealType>
0277 inline CELER_FUNCTION bool soft_mod(RealType dividend, RealType divisor)
0278 {
0279     auto remainder = std::fmod(dividend, divisor);
0280 
0281     SoftEqual<RealType> seq(detail::SoftEqualTraits<RealType>::rel_prec());
0282 
0283     return seq(0, remainder) || seq(divisor, remainder);
0284 }
0285 
0286 //---------------------------------------------------------------------------//
0287 }  // namespace celeritas