// Copyright (c) 2019, Vincent SAMY // All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // The views and conclusions contained in the software and documentation are those // of the authors and should not be interpreted as representing official policies, // either expressed or implied, of the FreeBSD Project. #pragma once #include "DigitalFilter.h" #include "math_utils.h" // Read this if you are an adulator of the math god: https://arxiv.org/pdf/1709.08321.pdf namespace difi { namespace details { // T: type // N: Number of points // Central differentiators: http://www.holoborodko.com/pavel/numerical-methods/numerical-derivative/central-differences/ template struct GetCDCoeffs { vectN_t operator()() const; }; template struct GetCDCoeffs { vectN_t operator()() const { return (vectN_t() << T(1), T(0), T(-1)).finished() / T(2); } }; template struct GetCDCoeffs { vectN_t operator()() const { return (vectN_t() << T(-1), T(8), T(0), T(8), T(1)).finished() / T(12); } }; template struct GetCDCoeffs { vectN_t operator()() const { return (vectN_t() << T(1), T(-9), T(45), T(0), T(-45), T(9), T(-1)).finished() / T(60); } }; template struct GetCDCoeffs { vectN_t operator()() const { return (vectN_t() << T(-3), T(32), T(-168), T(672), T(0), T(-672), T(168), T(-32), T(3)).finished() / T(840); } }; // Low-noise Lanczos differentiators: http://www.holoborodko.com/pavel/numerical-methods/numerical-derivative/lanczos-low-noise-differentiators/ template struct GetLNLCoeffs { vectN_t operator()() const { static_assert(N > 2 && N % 2 == 1, "'N' must be odd."); constexpr const size_t M = (N - 1) / 2; constexpr const size_t Den = M * (M + 1) * (2 * M + 1); vectN_t v{}; v(M) = T(0); for (size_t k = 0; k < M; ++k) { v(k) = T(3) * (M - k) / static_cast(Den); v(N - k - 1) = -v(k); } return v; } }; // Super Low-noise Lanczos differentiators: http://www.holoborodko.com/pavel/numerical-methods/numerical-derivative/lanczos-low-noise-differentiators/ template struct GetSLNLCoeffs { vectN_t operator()() const; }; template struct GetSLNLCoeffs { vectN_t operator()() const { return (vectN_t() << T(-22), T(67), T(58), T(0), T(-58), T(-67), T(22)).finished() / T(252); } }; template struct GetSLNLCoeffs { vectN_t operator()() const { return (vectN_t() << T(-86), T(142), T(193), T(126), T(0), T(-126), T(-193), T(-142), T(86)).finished() / T(1188); } }; template struct GetSLNLCoeffs { vectN_t operator()() const { return (vectN_t() << T(-300), T(294), T(532), T(503), T(296), T(0), T(-296), T(-503), T(-532), T(-294), T(300)).finished() / T(5148); } }; // Backward Noise-Robust differentiators; http://www.holoborodko.com/pavel/wp-content/uploads/OneSidedNoiseRobustDifferentiators.pdf template struct GetFNRCoeffs { vectN_t operator()() const { static_assert(N >= 2, "N should be greater than 2"); constexpr const size_t BinCoeff = N - 2; constexpr const size_t M = N / 2; vectN_t v{}; v(0) = T(1); v(M) = T(0); v(N - 1) = T(-1); for (size_t i = 1; i < M; ++i) { v(i) = Binomial(BinCoeff, i) - Binomial(BinCoeff, i - 1); v(N - i - 1) = -v(i); } v /= std::pow(T(2), T(BinCoeff)); return v; } }; // Backward Hybrid Noise-Robust differentiators; http://www.holoborodko.com/pavel/wp-content/uploads/OneSidedNoiseRobustDifferentiators.pdf template struct GetFHNRCoeffs { vectN_t operator()() const; }; template struct GetFHNRCoeffs { vectN_t operator()() const { return (vectN_t() << T(2), T(-1), T(-2), T(1)).finished() / T(2); } }; template struct GetFHNRCoeffs { vectN_t operator()() const { return (vectN_t() << T(7), T(1), T(-10), T(-1), T(3)).finished() / T(10); } }; template struct GetFHNRCoeffs { vectN_t operator()() const { return (vectN_t() << T(16), T(1), T(-10), T(-10), T(-6), T(9)).finished() / T(28); } }; template struct GetFHNRCoeffs { vectN_t operator()() const { return (vectN_t() << T(12), T(5), T(-8), T(-6), T(-10), T(1), T(6)).finished() / T(28); } }; template struct GetFHNRCoeffs { vectN_t operator()() const { return (vectN_t() << T(22), T(7), T(-6), T(-11), T(-14), T(-9), T(-2), T(13)).finished() / T(60); } }; template struct GetFHNRCoeffs { vectN_t operator()() const { return (vectN_t() << T(52), T(29), T(-14), T(-17), T(-40), T(-23), T(-26), T(11), T(28)).finished() / T(180); } }; template struct GetFHNRCoeffs { vectN_t operator()() const { return (vectN_t() << T(56), T(26), T(-2), T(-17), T(-30), T(-30), T(-28), T(-13), T(4), T(34)).finished() / T(220); } }; template struct GetFHNRCoeffs { vectN_t operator()() const { return (vectN_t() << T(320), T(206), T(-8), T(-47), T(-186), T(-150), T(-214), T(-103), T(-92), T(94), T(180)).finished() / T(1540); } }; template struct GetFHNRCoeffs { vectN_t operator()() const { return (vectN_t() << T(322), T(217), T(110), T(35), T(-42), T(-87), T(-134), T(-149), T(-166), T(-151), T(-138), T(-93), T(-50), T(28), T(98), T(203)).finished() / T(2856); } }; template vectN_t GetBackwardISDCoeffs() { vectN_t v{}; const vectN_t v0 = BackwardCoeffs{}(); for (Eigen::Index k = 0; k < N; ++k) v(k) = k * v0(k); return v(k); } // Backward Noise-Robust differentiators for irregular space data template struct GetFNRISDCoeffs { vectN_t operator()() const { return GetBackwardISDCoeffs>(); } }; // Backward Hybrid Noise-Robust differentiators for irregular space data template struct GetFHNRISDCoeffs { vectN_t operator()() const { return GetBackwardISDCoeffs>(); } }; // Centered Noise-Robust differentiators (tangency at 2nd order): http://www.holoborodko.com/pavel/numerical-methods/numerical-derivative/smooth-low-noise-differentiators/ template struct GetCNR2Coeffs { vectN_t operator()() const { static_assert(N % 2 == 1., "'N' must be odd."); return GetFNRCoeffs{}(); // Same coefficients } }; // Centered Noise-Robust differentiators (tangency at 4th order): http://www.holoborodko.com/pavel/numerical-methods/numerical-derivative/smooth-low-noise-differentiators/ template struct GetCNR4Coeffs { vectN_t operator()() const; }; template struct GetCNR4Coeffs { vectN_t operator()() const { return (vectN_t() << T(-5), T(12), T(39), T(0), T(-39), T(-12), T(5)).finished() / T(96); } }; template struct GetCNR4Coeffs { vectN_t operator()() const { return (vectN_t() << T(-2), T(-1), T(16), T(27), T(0), T(-27), T(-16), T(1), T(2)).finished() / T(96); } }; template struct GetCNR4Coeffs { vectN_t operator()() const { return (vectN_t() << T(-11), T(-32), T(39), T(256), T(322), T(0), T(-322), T(-256), T(-39), T(32), T(11)).finished() / T(1536); } }; // Centered Noise-Robust differentiators for irregular space data: http://www.holoborodko.com/pavel/numerical-methods/numerical-derivative/smooth-low-noise-differentiators/ template vectN_t GetCNRISDCoeffs() { constexpr const size_t M = (N - 1) / 2; vectN_t v{}; const vectN_t v0 = CNRCoeffs{}(); v(M) = 0; for (Eigen::Index k = 1; k < M; ++k) { v(M - k) = T(2) * k * v0(M - k); v(M + k) = T(2) * k * v0(M + k); } return v; } template struct GetCNR2ISDCoeffs { vectN_t operator()() const { return GetCNRISDCoeffs>(); } }; template struct GetCNR4ISDCoeffs { vectN_t operator()() const { return GetCNRISDCoeffs>(); } }; /* * Second order differentiators */ template constexpr T GetSONRBaseCoeff(size_t N, size_t M, size_t k) { if (k > M) return T(0); else if (k == M) return T(1); else return ((T(2) * N - T(10)) * GetSONRBaseCoeff(N, M, k + 1) - (N + T(2) * k + T(3)) * GetSONRBaseCoeff(N, M, k + 2)) / (N - T(2) * k - T(1)); } template vectN_t GetSONRBaseCoeffs() { static_assert(N >= 5 && N % 2 == 1, "N must be a odd number >= 5"); constexpr const size_t M = (N - 1) / 2; vectN_t s{}; for (size_t k = 0; k < M + 1; ++k) s(k) = GetSONRBaseCoeff(N, M, k); return s; } // Second-Order Centered Noise-Robust differentiator: http://www.holoborodko.com/pavel/downloads/NoiseRobustSecondDerivative.pdf template struct GetSOCNRCoeffs { vectN_t operator()() const { constexpr const size_t M = (N - 1) / 2; constexpr const T Den = pow(size_t(2), N - 3); vectN_t v{}; vectN_t s = GetSONRBaseCoeffs(); v(M) = s(0); for (size_t k = 1; k < M + 1; ++k) { v(M + k) = s(k); v(M - k) = s(k); } v /= Den; return v; } }; // Second-Order Backward Noise-Robust differentiator: http://www.holoborodko.com/pavel/downloads/NoiseRobustSecondDerivative.pdf template struct GetSOFNRCoeffs { vectN_t operator()() const { return GetSOCNRCoeffs(); } }; // Coefficients are the same. // Second-Order Centered Noise-Robust Irregular Space Data differentiator: http://www.holoborodko.com/pavel/downloads/NoiseRobustSecondDerivative.pdf template struct GetSOCNRISDCoeffs { vectN_t operator()() const { constexpr const size_t M = (N - 1) / 2; constexpr const T Den = pow(size_t(2), N - 3); vectN_t v{}; vectN_t s = GetSONRBaseCoeffs(); constexpr const alpha = [&s](size_t k) -> T { return T(4) * k * k * s(k) }; v(M) = -T(2) * alpha(0); for (size_t k = 1; k < M; ++k) { v(M + k) = alpha(k); v(M - k) = alpha(k); } v /= Den; return v; } }; // Second-Order Backward Noise-Robust Irregular Space Data differentiator: http://www.holoborodko.com/pavel/downloads/NoiseRobustSecondDerivative.pdf template struct GetSOFNRISDCoeffs { vectN_t operator()() const { return GetSOCNRISDCoeffs(); } }; // Same coefficients /* * Differentiator Generator */ template class BackwardDifferentiator : public GenericFilter { public: BackwardDifferentiator() : GenericFilter(vectX_t::Constant(1, T(1)), CoeffGetter{}()) {} BackwardDifferentiator(T timestep) : GenericFilter(vectX_t::Constant(1, T(1)), CoeffGetter{}() / std::pow(timestep, Order)) {} void setTimestep(T timestep) { setCoeffs(vectX_t::Constant(1, T(1)), CoeffGetter{}() / std::pow(timestep, Order)); } T timestep() const noexcept { return std::pow(bCoeff()(0) / CoeffGetter{}()(0), T(1) / Order); } }; template class CentralDifferentiator : public GenericFilter { public: CentralDifferentiator() : GenericFilter(vectX_t::Constant(1, T(1)), CoeffGetter{}(), FilterType::Centered) {} CentralDifferentiator(T timestep) : GenericFilter(vectX_t::Constant(1, T(1)), CoeffGetter{}() / std::pow(timestep, Order)) {} void setTimestep(T timestep) { setCoeffs(vectX_t::Constant(1, T(1)), CoeffGetter{}() / std::pow(timestep, Order)); } T timestep() const noexcept { return std::pow(bCoeff()(0) / CoeffGetter{}()(0), T(1) / Order); } }; template class TVBackwardDifferentiator : public TVGenericFilter { static_assert(Order >= 1, "Order must be greater or equal to 1"); public: TVBackwardDifferentiator() : TVGenericFilter(Order, vectX_t::Constant(1, T(1)), CoeffGetter{}()) {} }; template class TVCentralDifferentiator : public TVGenericFilter { static_assert(Order >= 1, "Order must be greater or equal to 1"); public: TVCentralDifferentiator() : TVGenericFilter(Order, vectX_t::Constant(1, T(1)), CoeffGetter{}(), FilterType::Centered) {} }; } // namespace details // Backward differentiators template using BackwardNoiseRobustDiff = details::BackwardDifferentiator>; template using BackwardHybridNoiseRobustDiff = details::BackwardDifferentiator>; // Time-Varying backward differentiators template using TVBackwardNoiseRobustDiff = details::TVBackwardDifferentiator>; template using TVBackwardHybridNoiseRobustDiff = details::TVBackwardDifferentiator>; // Central differentiators template using CentralDiff = details::CentralDifferentiator>; template using LowNoiseLanczosDiff = details::CentralDifferentiator>; template using SuperLowNoiseLanczosDiff = details::CentralDifferentiator>; template using CenteredNoiseRobust2Diff = details::CentralDifferentiator>; template using CenteredNoiseRobust4Diff = details::CentralDifferentiator>; // Time-Varying central differentiators template using TVCenteredNoiseRobust2Diff = details::TVCentralDifferentiator>; template using TVCenteredNoiseRobust4Diff = details::TVCentralDifferentiator>; // Second-order backward differentiators template using BackwardSecondOrderDiff = details::BackwardDifferentiator>; // Second-order Time-Varying backward differentiators template using TVBackwardSecondOrderDiff = details::TVBackwardDifferentiator>; // Second-order central differentiators template using CenteredSecondOrderDiff = details::CentralDifferentiator>; // Second-order Time-Varying central differentiators template using TVCenteredSecondOrderDiff = details::TVCentralDifferentiator>; } // namespace difi