ALFI
Advanced Library for Function Interpolation
Loading...
Searching...
No Matches
ratf.h
Go to the documentation of this file.
1#pragma once
2
3#include <cmath>
4
5#include "config.h"
6#include "util/numeric.h"
7#include "util/poly.h"
8
19namespace alfi::ratf {
25 template <typename Number = DefaultNumber, template <typename, typename...> class Container = DefaultContainer>
26 using RationalFunction = std::pair<Container<Number>, Container<Number>>;
27
36 template <typename Number = DefaultNumber, template <typename, typename...> class Container = DefaultContainer>
37 std::enable_if_t<!traits::has_size<Number>::value, Number>
38 val_mul(const RationalFunction<Number, Container>& rf, const Number& x) {
39 Number n = 0;
40 for (const auto& c : rf.first) {
41 n = n * x + c;
42 }
43 Number d = 0;
44 for (const auto& c : rf.second) {
45 d = d * x + c;
46 }
47 return n / d;
48 }
49
53 template <typename Number = DefaultNumber, template <typename, typename...> class Container = DefaultContainer>
54 std::enable_if_t<traits::has_size<Container<Number>>::value, Container<Number>>
55 val_mul(const RationalFunction<Number, Container>& rf, const Container<Number>& xx) {
56 Container<Number> result(xx.size());
57#if defined(_OPENMP) && !defined(ALFI_DISABLE_OPENMP)
58#pragma omp parallel for
59#endif
60 for (SizeT i = 0; i < xx.size(); ++i) {
61 result[i] = val_mul(rf, xx[i]);
62 }
63 return result;
64 }
65
74 template <typename Number = DefaultNumber, template <typename, typename...> class Container = DefaultContainer>
75 std::enable_if_t<!traits::has_size<Number>::value, Number>
76 val_div(const RationalFunction<Number, Container>& rf, const Number& x) {
77 const auto& numerator = rf.first;
78 const auto& denominator = rf.second;
79 Number n = 0;
80 for (auto i = numerator.rbegin(); i != numerator.rend(); ++i) {
81 n = n / x + *i;
82 }
83 Number d = 0;
84 for (auto i = denominator.rbegin(); i != denominator.rend(); ++i) {
85 d = d / x + *i;
86 }
87 const auto numerator_degree = numerator.empty() ? 0 : numerator.size() - 1;
88 const auto denominator_degree = denominator.empty() ? 0 : denominator.size() - 1;
89 if (numerator_degree >= denominator_degree) {
90 return n / d * util::numeric::binpow(x, numerator_degree - denominator_degree);
91 } else {
92 return n / d / util::numeric::binpow(x, denominator_degree - numerator_degree);
93 }
94 }
95
99 template <typename Number = DefaultNumber, template <typename, typename...> class Container = DefaultContainer>
100 std::enable_if_t<traits::has_size<Container<Number>>::value, Container<Number>>
101 val_div(const RationalFunction<Number, Container>& rf, const Container<Number>& xx) {
102 Container<Number> result(xx.size());
103#if defined(_OPENMP) && !defined(ALFI_DISABLE_OPENMP)
104#pragma omp parallel for
105#endif
106 for (SizeT i = 0; i < xx.size(); ++i) {
107 result[i] = val_div(rf, xx[i]);
108 }
109 return result;
110 }
111
117 template <typename Number = DefaultNumber, template <typename, typename...> class Container = DefaultContainer>
118 std::enable_if_t<!traits::has_size<Number>::value, Number>
119 val(const RationalFunction<Number, Container>& rf, const Number& x) {
120 if (std::abs(x) <= 1) {
121 return val_mul(rf, x);
122 } else {
123 return val_div(rf, x);
124 }
125 }
126
130 template <typename Number = DefaultNumber, template <typename, typename...> class Container = DefaultContainer>
131 std::enable_if_t<traits::has_size<Container<Number>>::value, Container<Number>>
132 val(const RationalFunction<Number, Container>& rf, const Container<Number>& xx) {
133 Container<Number> result(xx.size());
134#if defined(_OPENMP) && !defined(ALFI_DISABLE_OPENMP)
135#pragma omp parallel for
136#endif
137 for (SizeT i = 0; i < xx.size(); ++i) {
138 result[i] = val(rf, xx[i]);
139 }
140 return result;
141 }
142
169 template <typename Number = DefaultNumber, template <typename, typename...> class Container = DefaultContainer>
170 RationalFunction<Number,Container> pade(Container<Number> P, SizeT n, SizeT m, const Number& epsilon = std::numeric_limits<Number>::epsilon()) {
171 if constexpr (std::is_signed_v<SizeT>) {
172 if (n < 0 || m < 0) {
173 return {{}, {}};
174 }
175 }
176
178
179 Container<Number> Xnm1(n + m + 2, 0);
180 Xnm1[0] = 1;
181
182 // Modified extended Euclidean algorithm `gcd(a,b)=as+bt` without s variable
183 // a = Xnm1
184 // b = P
185 Container<Number> old_r, r, old_t, t;
186 if (Xnm1.size() >= P.size()) {
187 old_r = std::move(Xnm1), r = std::move(P);
188 old_t = {0}, t = {1};
189 } else {
190 old_r = std::move(P), r = std::move(Xnm1);
191 old_t = {1}, t = {0};
192 }
193
194 // `old_r.size()` strictly decreases, except maybe the first iteration
195 // ReSharper disable once CppDFALoopConditionNotUpdated
196 while (old_r.size() > n + 1) {
197 auto [q, new_r] = util::poly::div(old_r, r, epsilon);
198
199 const auto qt = util::poly::mul(q, t);
200 const auto new_t_size = std::max(old_t.size(), qt.size());
201 Container<Number> new_t(new_t_size, 0);
202 for (SizeT i = 0, offset = new_t_size - old_t.size(); i < old_t.size(); ++i) {
203 new_t[offset+i] = old_t[i];
204 }
205 for (SizeT i = 0, offset = new_t_size - qt.size(); i < qt.size(); ++i) {
206 new_t[offset+i] -= qt[i];
207 }
208
209 old_r = std::move(r);
210 r = std::move(new_r);
211 old_t = std::move(t);
212 t = std::move(new_t);
213
214 util::poly::normalize(old_r, epsilon);
215 }
216
217 util::poly::normalize(old_t, epsilon);
218
219 if (old_t.size() > m + 1 || std::abs(old_t[old_t.size()-1]) <= epsilon) {
220 return {{}, {}};
221 }
222
223 return std::make_pair(old_r, old_t);
224 }
225}
Namespace providing support for rational functions.
std::pair< Container< Number >, Container< Number > > RationalFunction
Represents a rational function , where and are polynomials.
Definition ratf.h:26
RationalFunction< Number, Container > pade(Container< Number > P, SizeT n, SizeT m, const Number &epsilon=std::numeric_limits< Number >::epsilon())
Computes the [n / m] Pade approximant of the polynomial P about the point .
Definition ratf.h:170
std::enable_if_t<!traits::has_size< Number >::value, Number > val_div(const RationalFunction< Number, Container > &rf, const Number &x)
Evaluates the rational function at a scalar point by factoring out powers of x.
Definition ratf.h:76
std::enable_if_t<!traits::has_size< Number >::value, Number > val(const RationalFunction< Number, Container > &rf, const Number &x)
Evaluates the rational function at a scalar point.
Definition ratf.h:119
std::enable_if_t<!traits::has_size< Number >::value, Number > val_mul(const RationalFunction< Number, Container > &rf, const Number &x)
Evaluates the rational function at a scalar point using Horner's method.
Definition ratf.h:38
Number binpow(Number x, ExponentType n)
Computes the power of a number using binary exponentiation.
Definition numeric.h:26
void normalize(Container< Number > &p, const Number &epsilon=std::numeric_limits< Number >::epsilon())
Normalizes a polynomial by removing leading zero coefficients.
Definition poly.h:27
std::pair< Container< Number >, Container< Number > > div(const Container< Number > &dividend, const Container< Number > &divisor, const Number &epsilon=std::numeric_limits< Number >::epsilon())
Divides one polynomial by another.
Definition poly.h:90
Container< Number > mul(const Container< Number > &p1, const Container< Number > &p2)
Multiplies two polynomials.
Definition poly.h:56
ALFI_DEFAULT_NUMBER DefaultNumber
Definition config.h:17
ALFI_SIZE_TYPE SizeT
Definition config.h:22
ALFI_DEFAULT_CONTAINER< Number > DefaultContainer
Definition config.h:20