SpECTRE Documentation Coverage Report
Current view: top level - Utilities - EqualWithinRoundoff.hpp Hit Total Coverage
Commit: a8efe75339f4781ca06d43fed14c40144d5e8a08 Lines: 2 5 40.0 %
Date: 2024-10-17 21:19:21
Legend: Lines: hit not hit

          Line data    Source code
       1           0 : // Distributed under the MIT License.
       2             : // See LICENSE.txt for details.
       3             : 
       4             : #pragma once
       5             : 
       6             : #include <algorithm>
       7             : #include <limits>
       8             : #include <type_traits>
       9             : 
      10             : #include "Utilities/Algorithm.hpp"
      11             : #include "Utilities/ConstantExpressions.hpp"
      12             : #include "Utilities/ErrorHandling/Assert.hpp"
      13             : #include "Utilities/ForceInline.hpp"
      14             : #include "Utilities/TypeTraits/IsIterable.hpp"
      15             : #include "Utilities/TypeTraits/IsMaplike.hpp"
      16             : 
      17             : namespace EqualWithinRoundoffImpls {
      18             : /*!
      19             :  * \brief Specialize this class to add support for the `equal_within_roundoff`
      20             :  * function.
      21             :  *
      22             :  * Ensure the `Lhs` and `Rhs` are symmetric. A specialization must implement a
      23             :  * static `apply` function with this signature:
      24             :  *
      25             :  * ```cpp
      26             :  * static bool apply(const Lhs& lhs, const Rhs& rhs, const double eps,
      27             :  *                   const double scale);
      28             :  * ```
      29             :  *
      30             :  * It can be helpful to invoke the `equal_within_roundoff` function for floating
      31             :  * points from within your specialization.
      32             :  */
      33             : template <typename Lhs, typename Rhs, typename = std::nullptr_t>
      34           1 : struct EqualWithinRoundoffImpl;
      35             : }  // namespace EqualWithinRoundoffImpls
      36             : 
      37             : /*!
      38             :  * \ingroup UtilitiesGroup
      39             :  * \brief Checks if two values `lhs` and `rhs` are equal within roundoff, by
      40             :  * comparing `abs(lhs - rhs) < (max(abs(lhs), abs(rhs)) + scale) * eps`.
      41             :  *
      42             :  * The two values can be floating-point numbers, or any types for which
      43             :  * `EqualWithinRoundoffImpls::EqualWithinRoundoffImpl` has been specialized. For
      44             :  * example, a default implementation exists for the case where `lhs`, `rhs`, or
      45             :  * both, are iterable, and compares the values point-wise.
      46             :  */
      47             : template <typename Lhs, typename Rhs>
      48           1 : constexpr SPECTRE_ALWAYS_INLINE bool equal_within_roundoff(
      49             :     const Lhs& lhs, const Rhs& rhs,
      50             :     const double eps = std::numeric_limits<double>::epsilon() * 100.0,
      51             :     const double scale = 1.0) {
      52             :   return EqualWithinRoundoffImpls::EqualWithinRoundoffImpl<Lhs, Rhs>::apply(
      53             :       lhs, rhs, eps, scale);
      54             : }
      55             : 
      56             : /// Specializations of `EqualWithinRoundoffImpl` for custom types, to add
      57             : /// support for the `equal_within_roundoff` function.
      58             : namespace EqualWithinRoundoffImpls {
      59             : 
      60             : // Compare two floating points
      61             : template <typename Floating>
      62             : struct EqualWithinRoundoffImpl<Floating, Floating,
      63             :                                Requires<std::is_floating_point_v<Floating>>> {
      64             :   static constexpr SPECTRE_ALWAYS_INLINE bool apply(const Floating& lhs,
      65             :                                                     const Floating& rhs,
      66             :                                                     const double eps,
      67             :                                                     const double scale) {
      68             :     return ce_fabs(lhs - rhs) <=
      69             :            (std::max(ce_fabs(lhs), ce_fabs(rhs)) + scale) * eps;
      70             :   }
      71             : };
      72             : 
      73             : // Compare a complex number to a floating point, interpreting the latter as a
      74             : // real number
      75             : template <typename Floating>
      76             : struct EqualWithinRoundoffImpl<std::complex<Floating>, Floating,
      77             :                                Requires<std::is_floating_point_v<Floating>>> {
      78             :   static SPECTRE_ALWAYS_INLINE bool apply(const std::complex<Floating>& lhs,
      79             :                                           const Floating& rhs, const double eps,
      80             :                                           const double scale) {
      81             :     return equal_within_roundoff(lhs.real(), rhs, eps, scale) and
      82             :            equal_within_roundoff(lhs.imag(), 0., eps, scale);
      83             :   }
      84             : };
      85             : 
      86             : // Compare a floating point to a complex number, interpreting the former as a
      87             : // real number
      88             : template <typename Floating>
      89             : struct EqualWithinRoundoffImpl<Floating, std::complex<Floating>,
      90             :                                Requires<std::is_floating_point_v<Floating>>> {
      91             :   static SPECTRE_ALWAYS_INLINE bool apply(const std::complex<Floating>& lhs,
      92             :                                           const Floating& rhs, const double eps,
      93             :                                           const double scale) {
      94             :     return equal_within_roundoff(rhs, lhs, eps, scale);
      95             :   }
      96             : };
      97             : 
      98             : // Compare two complex numbers
      99             : template <typename Floating>
     100             : struct EqualWithinRoundoffImpl<std::complex<Floating>, std::complex<Floating>,
     101             :                                Requires<std::is_floating_point_v<Floating>>> {
     102             :   static SPECTRE_ALWAYS_INLINE bool apply(const std::complex<Floating>& lhs,
     103             :                                           const std::complex<Floating>& rhs,
     104             :                                           const double eps,
     105             :                                           const double scale) {
     106             :     return equal_within_roundoff(lhs.real(), rhs.real(), eps, scale) and
     107             :            equal_within_roundoff(lhs.imag(), rhs.imag(), eps, scale);
     108             :   }
     109             : };
     110             : 
     111             : // Compare an iterable to a floating point
     112             : template <typename Lhs, typename Rhs>
     113             : struct EqualWithinRoundoffImpl<
     114             :     Lhs, Rhs,
     115             :     Requires<tt::is_iterable_v<Lhs> and not tt::is_maplike_v<Lhs> and
     116             :              std::is_floating_point_v<Rhs>>> {
     117             :   static SPECTRE_ALWAYS_INLINE bool apply(const Lhs& lhs, const Rhs& rhs,
     118             :                                           const double eps,
     119             :                                           const double scale) {
     120             :     return alg::all_of(lhs, [&rhs, &eps, &scale](const auto& lhs_element) {
     121             :       return equal_within_roundoff(lhs_element, rhs, eps, scale);
     122             :     });
     123             :   }
     124             : };
     125             : 
     126             : // Compare a floating point to an iterable
     127             : template <typename Lhs, typename Rhs>
     128             : struct EqualWithinRoundoffImpl<
     129             :     Lhs, Rhs,
     130             :     Requires<tt::is_iterable_v<Rhs> and not tt::is_maplike_v<Rhs> and
     131             :              std::is_floating_point_v<Lhs>>> {
     132             :   static SPECTRE_ALWAYS_INLINE bool apply(const Lhs& lhs, const Rhs& rhs,
     133             :                                           const double eps,
     134             :                                           const double scale) {
     135             :     return equal_within_roundoff(rhs, lhs, eps, scale);
     136             :   }
     137             : };
     138             : 
     139             : // Compare two iterables
     140             : template <typename Lhs, typename Rhs>
     141           0 : struct EqualWithinRoundoffImpl<
     142             :     Lhs, Rhs,
     143             :     Requires<tt::is_iterable_v<Lhs> and not tt::is_maplike_v<Lhs> and
     144             :              tt::is_iterable_v<Rhs> and not tt::is_maplike_v<Rhs>>> {
     145           0 :   static bool apply(const Lhs& lhs, const Rhs& rhs, const double eps,
     146             :                     const double scale) {
     147             :     auto lhs_it = lhs.begin();
     148             :     auto rhs_it = rhs.begin();
     149             :     while (lhs_it != lhs.end() and rhs_it != rhs.end()) {
     150             :       if (not equal_within_roundoff(*lhs_it, *rhs_it, eps, scale)) {
     151             :         return false;
     152             :       }
     153             :       ++lhs_it;
     154             :       ++rhs_it;
     155             :     }
     156             :     ASSERT(lhs_it == lhs.end() and rhs_it == rhs.end(),
     157             :            "Can't compare lhs and rhs because they have different lengths.");
     158             :     return true;
     159             :   }
     160             : };
     161             : 
     162             : }  // namespace EqualWithinRoundoffImpls

Generated by: LCOV version 1.14